【问题标题】:How to design a Settings model to generically get + set values for all UserDefaults keys如何设计一个设置模型来一般地获取所有 UserDefaults 键的 + 设置值
【发布时间】:2021-04-08 17:50:04
【问题描述】:

我正在设置我的 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&lt;name of setting&gt;()set&lt;name of setting&gt;() 通用化,我通过它传递一个设置键(仅此而已),然后它处理其他所有事情?

我已经确定每个设置都有这些共享元素:

  1. 设置键(例如我的示例中的 SettingsKey.TimeFormat)
  2. 唯一类型(可以是 AnyObject、String、Int、Bool 等,例如我的示例中的枚举 TimeFormat)
  3. 唯一属性(例如我的示例中的 timeFormat)
  4. 默认值(例如我的示例中的 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() 函数的值在用户在应用内更改时仍然需要。

【问题讨论】:

    标签: ios swift settings generic-programming userdefaults


    【解决方案1】:

    请注意,您的设置不必是存储属性。它们可以是计算属性:

    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
    }
    

    现在要添加一个新设置,您只需要为键编写一个新的枚举案例,以及另外两行代码。

    【讨论】:

      猜你喜欢
      • 2014-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-12
      • 2010-11-17
      • 2013-02-25
      • 2013-05-06
      • 1970-01-01
      相关资源
      最近更新 更多