NSManagedObject 和 Codable 用于存储在服务器和本地存储中的类

Posted

技术标签:

【中文标题】NSManagedObject 和 Codable 用于存储在服务器和本地存储中的类【英文标题】:NSManagedObject and Codable for class stored in server and local storage 【发布时间】:2018-09-22 17:35:13 【问题描述】:

我正在创建一个表示本地存储或来自在线的数据的类。在运行时,我将使用 get 请求从服务器获取这些对象的数组,并将它们与 CoreData 中的现有对象进行比较,以确定用于应用程序和存储的新对象集。

因此,我创建了一个代表我的对象的 CoreData 托管类,它继承了NSManagedObject。这将允许我处理本地存储,将数组放入存储中并在应用程序完成后放回一个新的。

然后我想使这个类可编码,这样我就可以实现required public init(from decoder: Decoder) 方法,该方法允许我在获取请求中获取这样的对象数组。 JSONDecoder().decode([DataClass].self, from: data!).

我尝试了两种方法,但它们都不起作用。

问题:

我在类范围内有一个private enum CodingKeys: String, CodingKey ...,使用它进行编码效果很好,但无论出于何种原因,都会触发一个错误,指出该类没有实现我无意实现的编码。我不知道为什么这很重要。

我可以将枚举放在初始化程序的范围内,但如果我这样做,就会弹出一个新问题。我必须调用NSManagedObject 的指定初始化程序。这是一个问题,因为该调用需要NSManagedObjectContext,而我没有。

作为替代方案:

我创建了一个方法func getDataClass(from decoder: Decoder) -> DataClass?,但这也带来了两个问题。我仍然必须以某种方式初始化 DataClass 对象,但我不知道如何将其带入 JSONDecoder().decode([DataClass].self, from: data!) 行。

【问题讨论】:

【参考方案1】:

你必须使DataClass符合Codable

首先创建两个扩展,以便能够传递userInfo 字典中的NSManagedObjectContext JSONDecoder

extension CodingUserInfoKey 
    static let context = CodingUserInfoKey(rawValue: "context")!


extension JSONDecoder 
    convenience init(context: NSManagedObjectContext) 
        self.init()
        self.userInfo[.context] = context
    


以下示例仅使用DataClass 中的一个属性nameCodable 方法必须在类中,而不是在 CoreDataProperties 扩展中。

class DataClass: NSManagedObject, Codable 

    private enum CodingKeys: String, CodingKey  case name 

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

    required convenience init(from decoder: Decoder) throws 
        guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else  fatalError("Error: NSManagedObject not specified!") 
        let entity = NSEntityDescription.entity(forEntityName: "DataClass", in: context)!
        self.init(entity: entity, insertInto: context)
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
    

要使用解码器,您必须使用初始化程序

let context = // get the managed object context
let decoder = JSONDecoder(context: context)

【讨论】:

这是一个很好的解决方案!但我有点困惑。当我们抓取实体和上下文时,我们实际上并没有使用它们或更改它们的任何内容,对吗?这只是一种解决方法。 使用此代码,您可以从 JSON 创建核心数据记录并从核心数据记录创建 JSON。如果您不需要后者,请将一致性更改为 Decodable 并删除 encode(to encoder 如果没有该方法,我会收到一条错误消息,指出该类不符合 Decodable。它就像是 NSManagedObject 或 Codable 的子类 而且我仍然需要上下文和实体才能使用指定的初始化器 如果DataClass 有一个[String][Int] 类型的属性成员,我们如何编码这些属性?也就是说,我们需要指定case ArrayOfStrings 和/或case ArrayOfInts

以上是关于NSManagedObject 和 Codable 用于存储在服务器和本地存储中的类的主要内容,如果未能解决你的问题,请参考以下文章

如何一起使用Codable和Coredata?

如何在 Core Data 中使用 swift 4 Codable?

如何在 Core Data 中使用 swift 4 Codable?

如何在 Core Data 中使用 swift 4 Codable?

Codable和CodingKeys

忽略 Codable 对象中的非 Codable 可选属性