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

Codable似乎是一个非常令人兴奋的function。 但是我想知道我们如何在Core Data中使用它? 特别是,是否有可能直接编码/解码从/到NSManagedObject JSON?

我尝试了一个非常简单的例子:

在这里输入图像说明

并自己定义了Foo

 import CoreData @objc(Foo) public class Foo: NSManagedObject, Codable {} 

但是像这样使用它:

 let json = """ { "name": "foo", "bars": [{ "name": "bar1", }], [{ "name": "bar2" }] } """.data(using: .utf8)! let decoder = JSONDecoder() let foo = try! decoder.decode(Foo.self, from: json) print(foo) 

编译器因此错误而失败:

 super.init isn't called on all paths before returning from initializer 

目标文件是定义Foo的文件

我想我可能做错了,因为我甚至没有通过一个NSManagedObjectContext ,但我不知道在哪里坚持它。

核心数据是否支持Codable

您可以使用CoreData对象的Codable接口对数据进行编码和解码,但不像使用普通的旧Swift对象时那样自动。 以下是如何使用Core Data对象直接实现JSON解码的方法:

首先,你让你的对象实现Codable。 该接口必须在对象上定义,而不是在扩展中定义。 你也可以在这个类中定义你的编码键。

 class MyManagedObject: NSManagedObject, Codable { @NSManaged var property: String? enum CodingKeys: String, CodingKey { case property = "json_key" } } 

接下来,你可以定义init方法。 这也必须在类方法中定义,因为解码协议需要init方法。

 required convenience init(from decoder: Decoder) throws { } 

但是,与托pipe对象一起使用的正确初始化程序是:

 NSManagedObject.init(entity: NSEntityDescription, into context: NSManagedObjectContext) 

所以,这里的秘诀就是使用userInfo字典将适当的上下文对象传递给初始值设定项。 要做到这一点,你需要用一个新的键来扩展CodingUserInfoKey结构体:

 extension CodingUserInfoKey { static let context = CodingUserInfoKey(rawValue: "context") } 

现在,你可以作为上下文的解码器:

 required convenience init(from decoder: Decoder) throws { guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError() } guard let entity = NSEntityDescription.entity(forEntityName: "MyManagedObject", in: context) else { fatalError() } self.init(entity: entity, in: context) let container = decoder.container(keyedBy: CodingKeys.self) self.property = container.decodeIfPresent(String.self, forKey: .property) } 

现在,当您为Managed Objects设置解码时,您需要传递适当的上下文对象:

 let data = //raw json data in Data object let context = persistentContainer.newBackgroundContext() let decoder = JSONDecoder() decoder.userInfo[.context] = context _ = try decoder.decode(MyManagedObject.self, from: data) //we'll get the value from another context using a fetch request later... try context.save() //make sure to save your data once decoding is complete 

要编码数据,您需要使用编码协议function做类似的事情。

CoreData是它自己的持久化框架,并且根据其详尽的文档,您必须使用它的指定的初始化器,并遵循一个特定的path来创build和存储对象。

但是,您仍然可以以有限的方式使用Codable ,就像您可以使用NSCoding

一种方法是用这两种协议中的任何一种解码一个对象(或一个结构体),并将它的属性转换成你为每个核心数据的文档创build的新的NSManagedObject实例。

另一种方式(这是非常普遍的)是仅将一种协议用于要存储在托pipe对象属性中的非标准对象。 所谓“非标准”,是指任何不符合模型中规定的核心数据标准属性types的东西。 例如, NSColor不能直接作为托pipe对象属性存储,因为它不是CD支持的基本属性types之一。 相反,您可以使用NSKeyedArchiver将颜色序列化为NSData实例,并将其作为Data属性存储在Managed Object中。 用NSKeyedUnarchiver反转这个过程。 这是简单的,有一个更好的方式来做到这一点核心数据(请参阅瞬态属性 ),但它说明了我的观点。

你也可以设想采用Encodable (构成Codable的两个协议Codable – 你可以猜对方的名字吗?)把一个Managed Object实例直接转换成JSON来共享,但是你必须指定编码键和你自己的定制encode实现,因为它不会由编译器自定义编码密钥自动合成。 在这种情况下,您只想指定要包含的键(属性)。

希望这可以帮助。