问题
How would one implement a struct that manages UserDefaults mappings in Swift? Right now I have some computed properties a, b, c, d of different types and corresponding keys that look like this:
enum UserDefaultsKeys {
a_key
b_key
...
}
var a: String {
get { UserDefaults.standard.string(forKey: UserDefaultsKeys.a_key.rawValue) }
set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.a_key.rawValue) }
}
var b: Int {
get { UserDefaults.standard.integer(forKey: UserDefaultsKeys.b_key.rawValue) }
set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.b_key.rawValue) }
}
...
What I would like to achieve instead is to implement a struct that has a key of type String and a generic type value.
The value get function should - depending on its type - chose wether to use UserDefaults.standard.string
or UserDefaults.standard.integer
so that I can just create a DefaultsVar with some key and everything else is managed automatically for me.
What I have so far is this:
struct DefaultsVar<T> {
let key: String
var value: T {
get {
switch self {
case is String: return UserDefaults.standard.string(forKey: key) as! T
case is Int: return UserDefaults.standard.integer(forKey: key) as! T
default: return UserDefaults.standard.float(forKey: key) as! T
}
}
set { UserDefaults.standard.set(newValue, forKey: key) }
}
}
I get the following error: "Cast from DefaultsVar to unrelated Type 'String' always fails. I am completely new to swift (and relatively new to programming) and don't really understand how to implement this the right way - or if this is even an approach that would be considered a good practice. Could someone please shine some light on this?
Thank you in advance!
回答1:
You can use a custom
Property wrapper:
@propertyWrapper
struct UserDefaultStorage<T: Codable> {
private let key: String
private let defaultValue: T
private let userDefaults: UserDefaults
init(key: String, default: T, store: UserDefaults = .standard) {
self.key = key
self.defaultValue = `default`
self.userDefaults = store
}
var wrappedValue: T {
get {
guard let data = userDefaults.data(forKey: key) else {
return defaultValue
}
let value = try? JSONDecoder().decode(T.self, from: data)
return value ?? defaultValue
}
set {
let data = try? JSONEncoder().encode(newValue)
userDefaults.set(data, forKey: key)
}
}
}
This wrapper can store/restore any kind of codable into/from the user defaults.
Usage
@UserDefaultStorage(key: "myCustomKey", default: 0)
var myValue: Int
iOS 14
SwiftUI has a similar wrapper (only for iOS 14) called @AppStorage
and it can be used as a state. The advantage of using this is that it can be used directly as a State
. But it requires SwiftUI
and it only works from the iOS 14.
来源:https://stackoverflow.com/questions/64557123/how-can-i-implement-a-generic-struct-that-manages-key-value-pairs-for-userdefaul