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

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

enter image description here

我自己定义了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对象的可编码接口对数据进行编码和解码,但它不像普通的旧swift对象那样自动.下面是如何直接用核心数据对象实现JSON解码:

首先,使对象实现可编码.此接口必须在对象上定义,而不是在扩展中定义.您还可以在此类中定义编码键.

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 {
}

但是,与托管对象一起使用的合适初始值设定项是:

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

所以,这里的秘密是使用userInfo字典将适当的上下文对象传递给初始值设定项.为此,您需要使用一个新键扩展CodingUserInfoKey struct :

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

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

required convenience init(from decoder: Decoder) throws {

    guard let context = decoder.userInfo[CodingUserInfoKey.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)
}

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

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

要对数据进行编码,需要使用encode协议函数执行类似的操作.

Swift相关问答推荐

DynamicView Content的修改视图?

AirPods手势不发送AVAudioApp静音状态通知

Swift UI在视图中排序不同的 struct

UITableView未显示类数组数据

如何消除SwiftUI中SF符号的填充

阴影动画的动画不流畅

Swift:iVar + Equatable上的协议约束

Swift通过设置变量B自动设置变量A的值

对实例方法appendInterpolation的调用没有完全匹配

协议元类型'SomeProtocol.Type'上无法使用静态成员'currency'

子视图未在父视图 SwiftUI 中触发禁用状态更新

用逻辑运算符保护让

如何在 SwiftUI 中用图像替换占位符文本?

使用协议捕获 SwiftUI 视图

SwiftUI 4 列表初始化程序在 iOS 中不可用

如何在 RxSwift 中获取 controlEvent 类型?

FirebaseStorage:如何删除目录

在 iOS 中使用 Swift 保存 PDF 文件并显示它们

为什么我可以转换为 NSManagedObject 但不能转换为我的实体类型?

是 swift 中另一个日期的同一周、月、年的日期