Skip to content

Instantly share code, notes, and snippets.

@tadija
Last active September 13, 2021 21:28
Show Gist options
  • Save tadija/7668382ef3e14cb304b31b6273ae8f65 to your computer and use it in GitHub Desktop.
Save tadija/7668382ef3e14cb304b31b6273ae8f65 to your computer and use it in GitHub Desktop.
AEStoredWrapper
/**
* https://gist.github.com/tadija/7668382ef3e14cb304b31b6273ae8f65
* Revision 2
* Copyright © 2021 Marko Tadić
* Licensed under the MIT license
*/
import Foundation
public struct AEStoredWrapper {
public static var storage: UserDefaults = .standard
}
// MARK: - Wrappers
@propertyWrapper
public struct StoredValue<T> {
public let key: String
public let value: T
public let storage: UserDefaults
public var wrappedValue: T {
get {
storage.object(forKey: key) as? T ?? value
}
set {
if let optional = newValue as? AnyOptional, optional.isNil {
storage.removeObject(forKey: key)
} else {
storage.set(newValue, forKey: key)
}
}
}
public init(
wrappedValue value: T,
key: String,
storage: UserDefaults = AEStoredWrapper.storage
) {
self.value = value
self.key = key
self.storage = storage
}
}
@propertyWrapper
public struct StoredRaw<T: RawRepresentable> {
public let key: String
public let value: T
public let storage: UserDefaults
public var wrappedValue: T {
get {
guard let object = storage.object(forKey: key) as? T.RawValue else {
return value
}
return T(rawValue: object) ?? value
}
set {
if let optional = newValue as? AnyOptional, optional.isNil {
storage.removeObject(forKey: key)
} else {
storage.set(newValue.rawValue, forKey: key)
}
}
}
public init(
wrappedValue value: T,
key: String,
storage: UserDefaults = AEStoredWrapper.storage
) {
self.value = value
self.key = key
self.storage = storage
}
}
@propertyWrapper
public struct StoredCodable<T: Codable> {
public let key: String
public let value: T
public let storage: UserDefaults
public var wrappedValue: T {
get {
guard let data = storage.object(forKey: key) as? Data else {
return value
}
if let decoded = try? JSONDecoder().decode(T.self, from: data) {
return decoded
} else {
assertionFailure("decoding failed for key: \(key)")
return value
}
}
set {
if let optional = newValue as? AnyOptional, optional.isNil {
storage.removeObject(forKey: key)
} else {
if let encoded = try? JSONEncoder().encode(newValue) {
storage.set(encoded, forKey: key)
} else {
assertionFailure("encoding failed for key: \(key)")
}
}
}
}
public init(
wrappedValue value: T,
key: String,
storage: UserDefaults = AEStoredWrapper.storage
) {
self.value = value
self.key = key
self.storage = storage
}
}
// MARK: - Convenience
public extension StoredValue where T: ExpressibleByNilLiteral {
init(key: String, storage: UserDefaults = AEStoredWrapper.storage) {
self.init(wrappedValue: nil, key: key, storage: storage)
}
}
public extension StoredRaw where T: ExpressibleByNilLiteral {
init(key: String, storage: UserDefaults = AEStoredWrapper.storage) {
self.init(wrappedValue: nil, key: key, storage: storage)
}
}
public extension StoredCodable where T: ExpressibleByNilLiteral {
init(key: String, storage: UserDefaults = AEStoredWrapper.storage) {
self.init(wrappedValue: nil, key: key, storage: storage)
}
}
// MARK: - Helpers
private protocol AnyOptional {
var isNil: Bool { get }
}
extension Optional: AnyOptional {
var isNil: Bool { self == nil }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment