如何在Swift中实现用于管理UserDefaults的键值对的通用结构? 属性包装器:用法

问题描述

如何在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.stringUserDefaults.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上运行。