如何在核心数据中使用swift 4编码?
Codable似乎是一个非常令人兴奋的特性。但我想知道我们如何在核心数据中使用它?特别是,是否可以直接从NSManagedObject对JSON进行编码/解码 我尝试了一个非常简单的例子: 并定义了我自己:如何在核心数据中使用swift 4编码?,swift,core-data,swift4,xcode9,codable,Swift,Core Data,Swift4,Xcode9,Codable,Codable似乎是一个非常令人兴奋的特性。但我想知道我们如何在核心数据中使用它?特别是,是否可以直接从NSManagedObject对JSON进行编码/解码 我尝试了一个非常简单的例子: 并定义了我自己: import CoreData @objc(Foo) public class Foo: NSManagedObject, Codable {} 但这样使用时: let json = """ { "name": "foo", "bars": [{ "na
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
,就像您可以使用NSCoding
一样
一种方法是使用这些协议之一对对象(或结构)进行解码,并将其属性传输到根据Core Data的文档创建的新NSManagedObject
实例中
另一种方法(非常常见)是仅对要存储在托管对象属性中的非标准对象使用其中一种协议。“非标准”是指任何不符合模型中指定的核心数据标准属性类型的内容。例如,NSColor
不能直接存储为托管对象属性,因为它不是CD支持的基本属性类型之一。相反,您可以使用NSKeyedArchiver
将颜色序列化为NSData
实例,并将其作为数据属性存储在托管对象中。使用NSKeyedUnarchiver
反向执行此过程。这太简单了,有一种更好的方法来处理核心数据(请参阅),但它说明了我的观点
您还可以考虑采用可编码
(构成可编码
的两个协议之一-您能猜出另一个协议的名称吗?)将托管对象实例直接转换为JSON以供共享,但您必须和您自己的自定义encode
实现进行转换,因为编译器不会使用自定义编码键自动合成它。在这种情况下,您希望只指定要包含的键(属性)
希望这能有所帮助。您可以使用CoreData对象的可编码接口来编码和解码数据,但它不像普通的旧swift对象那样自动。以下是如何直接使用核心数据对象实现JSON解码: 首先,使对象实现可编码。此接口必须在对象上定义,而不是在扩展中定义。还可以在此类中定义编码键
类MyManagedObject:NSManagedObject,可编码{
@NSVAR托管属性:字符串?
枚举编码键:字符串,编码键{
case property=“json\u key”
}
}
接下来,您可以定义init方法。这也必须在类方法中定义,因为可解码协议需要init方法
required便利初始化(来自解码器:解码器)抛出{
}
但是,与托管对象一起使用的适当初始值设定项是:
NSManagedObject.init(实体:NSEntityDescription,进入上下文:NSManagedObjectContext)
所以,这里的秘密是使用userInfo字典将适当的上下文对象传递到初始值设定项中。为此,您需要使用一个新键扩展codingUserInfo-key
结构:
extension codingUserInfo键{
静态let context=codingUserInfo键(rawValue:“context”)
}
现在,您可以作为上下文的解码器:
required便利初始化(来自解码器:解码器)抛出{
guard let context=decoder.userInfo[codingUserInfo.context!]作为?NSManagedObjectContext else{fatalError()}
guard let entity=NSEntityDescription.entity(在:context中名为“MyManagedObject”)else{fatalError()}
init(实体:实体,在:上下文中)
let container=decoder.container(keyedBy:CodingKeys.self)
self.property=container.decodeIfPresent(String.self,forKey:.property)
}
现在,为托管对象设置解码时,需要传递适当的上下文对象:
let data=//数据对象中的原始json数据
let context=persistentContainer.newBackgroundContext()
let decoder=JSONDecoder()
解码器.userInfo[.context]=上下文
_=try decoder.decode(MyManagedObject.self,from:data)//稍后我们将使用获取请求从另一个上下文获取值。。。
尝试context.save()//确保解码完成后保存数据
要对数据进行编码,您需要使用编码协议功能执行类似操作。Swift 4.2:
按照卡萨德莫拉的解决方案
guard let context=decoder.userInfo[.context]as?NSManagedObjectContext else{fatalError()}
应该是
guard let context=decoder.userInfo[codingUserInfo.context!]as?NSManagedObjectContext else{fatalError()}
这可以防止Xcode错误识别为数组切片问题的错误
编辑:使用隐式展开选项以消除每次使用时强制展开
.context
的需要。对于希望使用XCode的现代方法生成NSManagedObject
文件的人来说,这是另一种选择,我创建了一个decoderRapper
类来公开一个Decoder
对象,然后在符合jsondeconding
协议的对象中使用该对象:
class DecoderWrapper: Decodable {
let decoder:Decoder
required init(from decoder:Decoder) throws {
self.decoder = decoder
}
}
protocol JSONDecoding {
func decodeWith(_ decoder: Decoder) throws
}
extension JSONDecoding where Self:NSManagedObject {
func decode(json:[String:Any]) throws {
let data = try JSONSerialization.data(withJSONObject: json, options: [])
let wrapper = try JSONDecoder().decode(DecoderWrapper.self, from: data)
try decodeWith(wrapper.decoder)
}
}
extension MyCoreDataClass: JSONDecoding {
enum CodingKeys: String, CodingKey {
case name // For example
}
func decodeWith(_ decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
}
}
这可能只对没有任何非可选属性的模型有用,但它解决了我希望使用可解码的
的问题,而且还可以管理与核心数据的关系和持久性,而无需
let myjson = [ "name" : "Something" ]
let myObject = NSEntityDescription.insertNewObject(forEntityName: "MyCoreDataClass", into: myContext) as! MyCoreDataClass
do {
try myObject.decode(json: myjson)
}
catch {
// handle any error
}