问题描述
如何在Swift中实现一种管理UserDefaults映射的结构? 现在,我具有一些不同类型的计算属性a,b,c,d和相应的键,如下所示:
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) }
}
...
我想实现的是实现一个具有String类型的键和泛型类型值的结构。
值get函数应该-根据其类型-选择是否使用UserDefaults.standard.string
或UserDefaults.standard.integer
,以便我可以创建带有某些键的DefaultsVar,而其他所有内容都将由我自动管理。
到目前为止,我的情况是这样:
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) }
}
}
我收到以下错误:“从DefaultsVar转换为不相关的类型'String'总是失败。 我是新手(不熟悉编程),真的不太了解如何正确地实现此方法,或者即使这是一种被认为是好的做法,我也不知道。有人可以照一下吗?
提前谢谢!
解决方法
您可以使用自定义
属性包装器:
@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)
}
}
}
此包装器可以将任何一种可编码的代码存储/恢复到用户默认设置。
用法
@UserDefaultStorage(key: "myCustomKey",default: 0)
var myValue: Int
iOS 14
SwiftUI有一个类似的包装(仅适用于iOS 14),名为@AppStorage
,可以用作状态。使用它的好处是它可以直接用作State
。但是它需要SwiftUI
,并且只能在iOS 14上运行。