如何在核心数据中使用swift 4编码?

如何在核心数据中使用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

Codable似乎是一个非常令人兴奋的特性。但我想知道我们如何在核心数据中使用它?特别是,是否可以直接从NSManagedObject对JSON进行编码/解码

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

并定义了我自己:

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
}