如何在 Swift 中实现一个管理 UserDefaults 的键值对的通用结构?

Posted

技术标签:

【中文标题】如何在 Swift 中实现一个管理 UserDefaults 的键值对的通用结构?【英文标题】:How can I implement a generic struct that manages key-value pairs for UserDefaults in Swift? 【发布时间】: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 已经是通用的,只有少数支持的类型。 我认为它可能会阻止所有变量的一些样板代码。 【参考方案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,如果它是您的答案,请不要忘记 接受 ;)

以上是关于如何在 Swift 中实现一个管理 UserDefaults 的键值对的通用结构?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中实现全球化?

如何在 inputAccessoryView [Swift] 中实现滚动视图

如何在 Swift 中实现单例类

这是如何在 swift 4 中实现的?

如何使用 Swift 在 CustomCell 中实现 inputAccessoryView 到 UITextField?

如何在 quickblox (Swift, iOS, xcode) 中实现发送图片