-
Notifications
You must be signed in to change notification settings - Fork 0
Extend
In this chapter, we are going to study how to wrap your container with EFStorage.
// If you integrated using SPM, you should
import EFStorageCore
// if with CocoaPods, then
import EFStorage
IMPORTANT: remember to replace placeholder
<#ExampleStorage#>
with your underlying storage name!
extension <#ExampleStorage#>: EFUnderlyingStorage {
public class func makeDefault() -> Self {
// ... return the default storage
}
}
In case if your class/struct may fail the initialization process,
you could conform to EFFailableUnderlyingStorage
instead.
extension <#ExampleStorage#>: EFFailableUnderlyingStorage {
public class func makeDefault() -> Self? {
// ... return the default storage or nil
}
}
public protocol <#ExampleStorage#>Storable {
func as<#ExampleStorage#>Storable() -> <#ExampleStorage#>Storable//or ! or ?, depends on your usage
static func from<#ExampleStorage#>(_ <#exampleStorage#>: <#ExampleStorage#>, forKey key: String) -> Self?
}
then conform type to <#ExampleStorage#>Storable
to be stored.
Since you have total control of this protocol, so returning
Result<StorableType, Error>
for type safety (like those that comes with EFStorage by default), or even writing a protocol that looks nothing like the one above, all of them are totally acceptable as long as it fits your need.
Due to the original implementation of EFStorage, it has to be a singleton.
public class EFStorage<#ExampleStorage#>Ref<Content: <#ExampleStorage#>Storable>: EFSingleInstanceStorageReference {
public required init(
iKnowIShouldNotCallThisDirectlyAndIsResponsibleForUnexpectedBehaviorMyself: Bool,
forKey key: String, in storage: <#ExampleStorage#>//? // if your storage may fail to initialize
) {
self.key = key
self.storage = storage
self.content = Content.from<#ExampleStorage#>(storage, forKey: key)
}
public let key: String
public let storage: <#ExampleStorage#>//?
public var content: Content? {
didSet {
if let newValue = content {
storage/*?*/.set(newValue.as<#ExampleStorage#>Storable(), forKey: key)
} else {
storage/*?*/.removeObject(forKey: key)
}
}
}
}
This is not required, but highly recommended.
@propertyWrapper
public struct EFStorage<#ExampleStorage#><Content: <#ExampleStorage#>Storable>: EFSingleInstanceStorageReferenceWrapper {
public var _ref: EFStorage<#ExampleStorage#>Ref<Content>
public var wrappedValue: Content {
get {
if let content = _ref.content { return content }
let defaultContent = makeDefaultContent()
if persistDefaultContent { _ref.content = defaultContent }
return defaultContent
}
set { _ref.content = newValue }
}
public let persistDefaultContent: Bool
public let makeDefaultContent: () -> Content
public func removeContentFromUnderlyingStorage() {
_ref.content = nil
}
public init(
__ref: EFStorage<#ExampleStorage#>Ref<Content>,
makeDefaultContent: @escaping () -> Content,
persistDefaultContent: Bool
) {
self._ref = __ref
self.makeDefaultContent = makeDefaultContent
self.persistDefaultContent = persistDefaultContent
}
}
Again, this is optional, but can be useful
public extension EFUnderlyingStorageWrapper {
subscript<T: <#ExampleStorage#>Storable>(
dynamicMember key: String
) -> T? where Base == <#ExampleStorage#> {
get {
return EFStorage<#ExampleStorage#>Ref.forKey(key, in: base).content
}
set {
EFStorage<#ExampleStorage#>Ref.forKey(key, in: base).content = newValue
}
}
subscript<T: <#ExampleStorage#>Storable>(
dynamicMember key: String
) -> T? where Base == <#ExampleStorage#>.Type {
get {
return EFStorage<#ExampleStorage#>Ref.forKey(key).content
}
set {
EFStorage<#ExampleStorage#>Ref.forKey(key).content = newValue
}
}
}