我在Swift中有一个[String: Codable]字典,我想保存到用户默认值中,但是我在努力做到这一点。

我尝试使用将其转换为Data

try! JSONSerialization.data(withJSONObject: dictionary, options: .init(rawValue: 0))

但这会导致崩溃(“ JSON写入(_SwiftValue)中的类型无效”)

我试过使用JSONEncoder

JSONEncoder().encode(dictionary)

但这不会编译(“无法推断通用参数T”)。

当然,我可以将所有的Codables手动转换为[String:Any],然后将其写入用户默认值,但是由于Codable的全部目的是使“解码”和“编码”变得容易,所以我不确定为什么上述两种解决方案都是不可能(尤其是第二个)?

范例

为了重现性,您可以在Playground中使用以下代码:

import Foundation

struct A: Codable {}
struct B: Codable {}

let dict = [ "a": A(), "b": B() ] as [String : Codable]
let data = try JSONEncoder().encode(dict)

#1楼 票数:2 已采纳

作为一般约束, CodableAny是不可编码的。 使用结构而不是字典:

struct A: Codable {
    let a = 0
}
struct B: Codable {
    let b = "hi"
}
struct C: Codable {
    let a: A
    let b: B
}

let d = C(a: A(), b: B())
let data = try JSONEncoder().encode(d)

#2楼 票数:0

UserDefaults有一种保存[String:Any]字典的方法:

let myDictionary: [String: Any] = ["a": "one", "b": 2]
UserDefaults.standard.set(myDictionary, forKey: "key")
let retrievedDictionary: [String: Any] = UserDefaults.standard.dictionary(forKey: "key")!
print(retrievedDictionary)      // prints ["a": one, "b": 2]

但是,如果字典是要保存到UserDefaults的对象的属性,则需要为该对象实现Codable协议。 我知道的最简单的方法是使用JSONSerialization将字典转换为Data对象。 以下代码对我有用:

class MyObject: Codable {

    let dictionary: [String: Any]

    init(dictionary: [String: Any]) {
        self.dictionary = dictionary
    }

    enum CodingKeys: String, CodingKey {
        case dictionary
    }

    public required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        if values.contains(.dictionary), let jsonData = try? values.decode(Data.self, forKey: .dictionary) {
            dictionary = (try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any]) ??  [String: Any]()
        } else {
            dictionary = [String: Any]()
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        if !dictionary.isEmpty, let jsonData = try? JSONSerialization.data(withJSONObject: dictionary) {
            try container.encode(jsonData, forKey: .dictionary)
        }
    }
}

要从UserDefaults保存和检索MyObject ,可以执行以下操作:

extension UserDefaults {

    func set(_ value: MyObject, forKey defaultName: String) {
        guard let data = try? PropertyListEncoder().encode(value) else { return }
        set(data, forKey: defaultName)
    }

    func myObject(forKey defaultName: String) -> MyObject? {
        guard let data = data(forKey: defaultName) else { return nil }
        return try? PropertyListDecoder().decode(MyObject.self, from: data)
    }
}

  ask by BlackWolf translate from so

未解决问题?本站智能推荐:

3回复

如何使用包含已编码值的Encodable在Swift中对结构进行编码

想象一个如下的数据结构,在contents中包含一个值,它是一个已经编码的 JSON 片段。let partial = """{ "foo": "Foo", "bar": 1 }"""struct Document { let contents: String let other: [Strin
3回复

使用Swift的Encodable将可选属性编码为null,无需自定义编码

我想使用符合Encodable协议的struct使用 Swift 的JSONEncoder对可选字段进行编码。 默认设置是JSONEncoder使用encodeIfPresent方法,这意味着从 Json 中排除nil值。 如何在不编写我的自定义encode(to encoder: Encoder)
1回复

如何在没有密钥的情况下在Swift5可解码协议中解码具有字典数组类型的属性?

下面的 JSON 响应没有字典数组的键和内容。 我想用用下面结构可解此协议解码JSON 并使用下面的解码响应 要么 但得到错误 “预期解码 Dictionary<String, Any> 但找到了一个字符串/数据。” 如何在没有密钥的情况下解码包含字典内容数组的此类 JSON 响应?
2回复

如何以自定义方式编码已经符合Codable的基础类型(例如TimeInterval)?

在Swift 4中,以自定义方式将自定义类型编码为JSON很容易,并且提供了很好的文档。 但我无法以自定义方式编码Foundation类型。 我的具有TimeInterval类型属性的自定义类型需要是Codable ,其JSON格式显示如下所示的分钟和秒: 如果我为每个类型提供encod
1回复

对模型中没有嵌套属性的嵌套对象进行编码

假设我有以下结构: 在我的应用程序中,允许用户创建事件列表。 用户完成后,我想通过 POST 请求将该列表传递给我的服务器。 为此,我需要创建一个如下所示的有效 JSON 对象。 这是一个以"events"开头的Event列表。 如何以JSONEncoder.encode(events)将返回上面预
4回复

使用JSONEncoder编码/解码符合协议的类型数组

我正在尝试使用 Swift 4 中的新 JSONDecoder/Encoder 找到对符合 swift 协议的结构数组进行编码/解码的最佳方法。 我编了一个小例子来说明这个问题: 首先我们有一个协议标签和一些符合这个协议的类型。 然后我们有一个类型文章,它有一个标签数组。 最后我们对文章进行编码或解
1回复

在Swift中使用来自json的相同键解码字典

我有一个我在 JSON 文件中描述的结构,假设该结构名为Dog 我在 JSON 文件中的字典中跟踪我的狗,其中每条狗的名字充当标识符。 所以我可以有下一个 JSON: 为了描述我的 2 只狗,它是我(显然)想要避免重复键的字典,所以我希望当我解码[String: Dog]类型的myDogs ,如果
1回复

SwiftCodable:字典数组

我有一个来自Yelp的JSON对象,我无法弄清楚如何使用Swift Codable访问categories的title 。 这是JSON(为了便于阅读,删除了一些元素): 这是在JSON Viewer中 我访问第一级的属性,如name很容易,我访问lattitude和longitude