Skip to content

Instantly share code, notes, and snippets.

@WorldDownTown
Created December 27, 2019 06:40
Show Gist options
  • Save WorldDownTown/57382f9100af07d6e1f64c3ebe2e670a to your computer and use it in GitHub Desktop.
Save WorldDownTown/57382f9100af07d6e1f64c3ebe2e670a to your computer and use it in GitHub Desktop.
Property Wrapper Sample with UserDefaults
// refers to https://qiita.com/imk2o/items/1771682e9665865851e1
import Foundation
protocol UserDefaultConvertible {
init?(with object: Any)
func object() -> Any?
}
extension UserDefaultConvertible {
init?(with object: Any) {
guard let value = object as? Self else { return nil }
self = value
}
func object() -> Any? { self }
}
extension String: UserDefaultConvertible {}
extension Int: UserDefaultConvertible {}
extension Bool: UserDefaultConvertible {}
extension UserDefaultConvertible where Self: Codable {
init?(with object: Any) {
guard let value = (object as? Data).flatMap({ try? JSONDecoder().decode(Self.self, from: $0) }) else { return nil }
self = value
}
func object() -> Any? {
try? JSONEncoder().encode(self)
}
}
extension Optional: UserDefaultConvertible where Wrapped: UserDefaultConvertible {
init?(with object: Any) {
guard let value = Wrapped(with: object) else { return nil }
self = .some(value)
}
func object() -> Any? {
switch self {
case .some(let value):
return value.object()
case .none:
return nil
}
}
}
extension Array: UserDefaultConvertible where Element: UserDefaultConvertible {
init?(with object: Any) {
guard let value = (object as? [Any])?.compactMap(Element.init(with:)) else { return nil }
self = value
}
func object() -> Any? {
compactMap { $0.object() }
}
}
struct UserDefaultTypedKey<T>: RawRepresentable, ExpressibleByStringLiteral {
let rawValue: String
init(rawValue v: String) { rawValue = v }
init(stringLiteral v: String) { rawValue = v }
static var username: UserDefaultTypedKey<String?> { "username" }
}
@propertyWrapper
struct UserDefault<Value: UserDefaultConvertible> {
let key: UserDefaultTypedKey<Value>
let defaultValue: Value
let defaults: UserDefaults
init(_ key: UserDefaultTypedKey<Value>, defaultValue: Value, defaults: UserDefaults = .standard) {
self.key = key
self.defaultValue = defaultValue
self.defaults = defaults
}
private static var defaults: UserDefaults { .standard }
var wrappedValue: Value {
get {
defaults.object(forKey: key.rawValue).flatMap(Value.init(with:))
?? defaultValue
}
set {
newValue.object().map { defaults.set($0, forKey: key.rawValue) }
?? defaults.removeObject(forKey: key.rawValue)
}
}
}
struct Foo {
@UserDefault(.username, defaultValue: nil)
var username: String?
}
UserDefaults.standard.removeObject(forKey: "username")
var foo: Foo = .init()
foo.username // nil
foo.username = "hello"
foo.username // "hello"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment