Created
December 27, 2019 06:40
-
-
Save WorldDownTown/57382f9100af07d6e1f64c3ebe2e670a to your computer and use it in GitHub Desktop.
Property Wrapper Sample with UserDefaults
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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