如何解决如何设计一个设置模型来为所有 UserDefaults 键一般获取 + 设置值
我正在设置我的 Settings 类,它从 UserDefaults 获取/设置值。我希望尽可能多的代码是通用的,以尽量减少引入新设置时所涉及的工作(我有很多,我希望将来还会有更多),从而减少任何人为错误/错误的可能性.
我遇到了 this answer 来为 UserDefaults 创建一个包装器:
struct UserDefaultsManager {
static var userDefaults: UserDefaults = .standard
static func set<T>(_ value: T,forKey: String) where T: encodable {
if let encoded = try? JSONEncoder().encode(value) {
userDefaults.set(encoded,forKey: forKey)
}
}
static func get<T>(forKey: String) -> T? where T: Decodable {
guard let data = userDefaults.value(forKey: forKey) as? Data,let decodedData = try? JSONDecoder().decode(T.self,from: data)
else { return nil }
return decodedData
}
}
我创建了一个枚举来存储所有设置键:
enum SettingKeys: String {
case TimeFormat = "TimeFormat"
// and many more
}
每个设置都有自己的枚举:
enum TimeFormat: String,Codable {
case ampm = "12"
case mili = "24"
}
在这个简化的 Settings 类示例中,您可以看到,当它被实例化时,我初始化了每个定义的设置的值。我检查它的设置键是否在 UserDefaults 中找到:如果是,我使用找到的值,否则我将其设置为默认值并第一次将其保存到 UserDefaults。
class Settings {
var timeFormat: TimeFormat!
init() {
self.initTimeFormat()
}
func initTimeFormat() {
guard let format: TimeFormat = UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) else {
self.setTimeFormat(to: .ampm)
return
}
self.timeFormat = format
}
func setTimeFormat(to format: TimeFormat) {
UserDefaultsManager.set(format,forKey: SettingKeys.TimeFormat.rawValue)
self.timeFormat = format
}
}
这工作正常,而且非常简单。但是,提前考虑一下,为这个应用程序中的每个设置(以及我希望将来做的所有其他设置)复制这将是乏味的(因此容易出错)。有没有办法将 init<name of setting>()
和 set<name of setting>()
泛化,即我向它传递一个设置的键(仅此而已),然后它处理其他所有事情?
我已经确定每个设置都有这些共享元素:
- 设置键(例如我的示例中的 SettingsKey.TimeFormat)
- 唯一类型(可以是 AnyObject、String、Int、Bool 等。例如,在我的示例中为 enum TimeFormat)
- 独特的属性(例如我的示例中的 timeFormat)
- 默认值(例如我的示例中的 TimeFormat.ampm)
一如既往的感谢!
更新:
这可能会也可能不会有所不同,但考虑到所有设置都有一个默认值,我意识到它们不需要是非可选的可选项,但可以在初始化时设置默认值。
即改变:
var timeFormat: TimeFormat!
func initTimeFormat() {
guard let format: TimeFormat = UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) else {
self.setTimeFormat(to: .ampm)
return
}
self.timeFormat = format
}
致:
var timeFormat: TimeFormat = .ampm
func initTimeFormat() {
guard let format: TimeFormat = UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) else {
UserDefaultsManager.set(self.timeFormat,forKey: SettingKeys.TimeFormat.rawValue)
return
}
self.timeFormat = format
}
当用户在应用内更改其值时,仍需要 setTimeFormat()
函数。
解决方法
请注意,您的设置不必是存储的属性。它们可以是计算属性:
var timeFormat: TimeFormat {
get {
UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) ?? .ampm
}
set {
UserDefaultsManager.set(newValue,forKey: SettingKeys.TimeFormat.rawValue)
}
}
当您想要添加新设置时,您不必以这种方式编写尽可能多的代码。因为您将只添加一个新的设置键枚举案例和一个计算属性。
此外,可以将此计算属性包装到属性包装器中:
@propertyWrapper
struct FromUserDefaults<T: Codable> {
let key: SettingKeys
let defaultValue: T
init(key: SettingKeys,defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
UserDefaultsManager.get(forKey: key.rawValue) ?? defaultValue
}
set {
UserDefaultsManager.set(newValue,forKey: key.rawValue)
}
}
}
用法:
class Settings {
@FromUserDefaults(key: .TimeFormat,defaultValue: .ampm)
var timeFormat: TimeFormat
}
现在要添加新设置,您只需要为键编写一个新的枚举大小写,以及另外两行代码。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。