如何在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
实现,因为它不会由编译器自定义编码密钥自动合成。 在这种情况下,您只想指定要包含的键(属性)。
希望这可以帮助。