【问题标题】:How can I implement a generic struct that manages key-value pairs for UserDefaults in Swift?如何在 Swift 中实现一个管理 UserDefaults 的键值对的通用结构?
【发布时间】:2021-02-09 21:22:15
【问题描述】:

如何在 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) }
}
...

我想要实现的是实现一个结构,该结构具有字符串类型的键和泛型类型值。 值获取函数应该 - 根据它的类型 - 选择使用 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' 总是失败。 我对 swift 完全陌生(对编程也相对较新)并且并不真正了解如何以正确的方式实现这一点 - 或者这甚至是一种被认为是一种好的做法。有人可以对此有所了解吗?

提前谢谢你!

【问题讨论】:

  • 有什么好处? UserDefaults 的 setter 已经是通用的,只有少数支持的类型。
  • 我认为它可能会阻止所有变量的一些样板代码。

标签: swift generics data-structures struct


【解决方案1】:

您可以使用自定义

属性包装器:

@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 上使用。

【讨论】:

  • 无需从Any 转换为DataUserDefaults 有一个特定的方法来准确地从中检索数据。 data(forKey:).
  • 是的,你是对的@LeoDabus。我应该更新我的要点 :) 谢谢
  • 太棒了!谢谢您的回答!但是有一个问题:如果没有值可以返回,返回一个默认值(从而隐藏可能发生的错误)而不是抛出一个错误真的更可取吗?
  • 类本身总是返回一个默认值。顺便说一句,我们说的是用户 Defaults 对吗? ;)
  • 不客气。如果它对您有帮助,请不要忘记 upvote,如果它是您的答案,请不要忘记 接受 ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-05
  • 2018-05-14
  • 1970-01-01
  • 2023-02-03
  • 1970-01-01
相关资源
最近更新 更多