Swift - 将 [String: Any] 数组保存到 NSUserDefauls

Posted

技术标签:

【中文标题】Swift - 将 [String: Any] 数组保存到 NSUserDefauls【英文标题】:Swift - Save array of [String: Any] to NSUserDefauls 【发布时间】:2019-07-30 11:43:54 【问题描述】:

我发现这个扩展使用 Codable 被保存到 NSUserDefaults

extension UserDefaults 
    func decode<T : Codable>(for type : T.Type, using key : String) -> T? 
        let defaults = UserDefaults.standard
        guard let data = defaults.object(forKey: key) as? Data else return nil
        let decodedObject = try? PropertyListDecoder().decode(type, from: data)
        return decodedObject
    

    func encode<T : Codable>(for type : T, using key : String) 
        let defaults = UserDefaults.standard
        let encodedData = try? PropertyListEncoder().encode(type)
        defaults.set(encodedData, forKey: key)
        defaults.synchronize()
    

但我看到我有一个错误Type 'OfflineRequest' does not conform to protocol 'Decodable' 看起来像是因为Any

我要保存下一个结构:

struct OfflineRequest: Codable 
    let url: String
    let params: [String: Any]

这个想法是持久存储由于任何连接问题而失败的请求的堆栈(数组)。因此,我有 Core Data 数据模型,并且在将其发送到服务器之前将其属性转换为 [String: Any]。但现在我想创建离线优先算法。因此,如果用户离线,我想持久存储 [String: Any] 的 url 和参数。在这种情况下如何正确处理does not conform to protocol 'Decodable'

【问题讨论】:

为什么不只存储一个 json 格式的字符串并将其用于编码和解码? 如果您已经在使用 Core Data,为什么不使用它来存储您的请求而不是滥用用户默认值? @Paulw11 感谢 Paul 的评论,但解释起来非常简单。所以我有一个堆栈或一个队列请求数组,它们实际上是我想发送到服务器的所有操作,但它们失败了。因此,如果我使用带有 isSync 标志或任何其他标志的核心数据对象,我仍然可以在离线时修改适当对象的原始记录,当连接恢复时,我将发送修改后的对象以防它发生任何变化,我什至可以删除一些记录,所以我需要有复杂的算法来搜索我尚未同步的依赖项并更新它们。对吗? 我理解你想要做什么。我的建议是您将挂起的操作存储在 Core Data 中的另一种实体类型中,而不是用户默认值。 @Paulw11 好的,知道了!谢谢! 【参考方案1】:

您可以使用JSONSerialization.data(withJSONObject: Any) 对您的字典进行编码,并使用JSONSerialization.JSONObject(with: Data) 对其进行解码。你只需要确保你传递了一个有效的 json 对象(在这种情况下是一个字典),如果你传递一个类型为 Date 的字典,它就会崩溃。试试这样:

struct OfflineRequest: Codable 
    let url: String
    let params: [String: Any]


extension OfflineRequest 
    init(from decoder: Decoder) throws 
        var container = try decoder.unkeyedContainer()
        url = try container.decode(String.self)
        params = try JSONSerialization.jsonObject(with: container.decode(Data.self)) as? [String:Any] ?? [:]
    

    func encode(to encoder: Encoder) throws 
        var container = encoder.unkeyedContainer()
        try container.encode(url)
        try container.encode(JSONSerialization.data(withJSONObject: params))
    


let test = [OfflineRequest(url: "http://www.google.com", params: ["a":"1","b":"test", "c":["d":"test"]])]

let data = try! JSONEncoder().encode(test)
let loaded = try! JSONDecoder().decode([OfflineRequest].self, from: data)
print(loaded) // [__lldb_expr_3.OfflineRequest(url: "http://www.google.com", params: ["b": test, "a": 1, "c": \n    d = test;\n])]\n"

【讨论】:

【参考方案2】:

由于[String:Any],您收到错误建议您将[String:Any] 转换为Data 使用JSONSerialization.data

然后你就可以这样使用了

struct OfflineRequest: Codable 
    let url: String
    let params: Data


    enum CodingKeys: String, CodingKey 
        case url
        case params

    

    init(from decoder: Decoder) throws 
        let values = try decoder.container(keyedBy: CodingKeys.self)
        params = try values.decode(Data.self, forKey: .params)
        url = try values.decode(String.self, forKey: .url)
    

    func encode(to encoder: Encoder) throws 
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(url, forKey: .url)
        try container.encode(params, forKey: .params)
    

【讨论】:

以上是关于Swift - 将 [String: Any] 数组保存到 NSUserDefauls的主要内容,如果未能解决你的问题,请参考以下文章

Swift 3.0:数据到JSON [String:Any]

typeMismatch(Swift.Dictionary<Swift.String, Any>,

如何在 Swift 中访问 [String: Any] 字典上的值

Swift 3 Type 'Any' 没有下标成员

将 [Dictionary<String,Any?>] 转换为 [Dictionary<String,Any!>] ?迅速 3

Swift 无法将 NSNumber 桥接到 Int