Cómo usar swift 4 Codable en Core Data?

Codable parece una característica muy emocionante. Pero me pregunto cómo podemos usarlo en Core Data? En particular, ¿es posible codificar / decodificar directamente un JSON desde / hacia un NSManagedObject?

Intenté un ejemplo muy simple:

enter image description here

y definí Foo yo mismo:

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

Pero al usarlo así:

 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) 

El comstackdor falló con este error:

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

y el archivo de destino fue el archivo que definió Foo

Supongo que probablemente lo hice mal, ya que ni siquiera NSManagedObjectContext un NSManagedObjectContext , pero no tengo idea de dónde colocarlo.

¿Core Data es compatible con Codable ?

    Puede usar la interfaz codificable con objetos CoreData para codificar y decodificar datos, sin embargo, no es tan automático como cuando se usa con objetos rápidos simples. Aquí se explica cómo puede implementar la deencoding JSON directamente con los objetos de datos centrales:

    Primero, haces que tu objeto implemente Codable. Esta interfaz debe definirse en el objeto y no en una extensión. También puede definir sus claves de encoding en esta clase.

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

    Luego, puedes definir el método init. Esto también debe definirse en el método de clase porque el protocolo de deencoding requiere el método init.

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

    Sin embargo, el inicializador adecuado para su uso con objetos gestionados es:

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

    Entonces, el secreto aquí es usar el diccionario userInfo para pasar el objeto de contexto apropiado al inicializador. Para hacer esto, deberá extender la estructura CodingUserInfoKey con una nueva clave:

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

    Ahora, puedes usar el decodificador para el contexto:

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

    Ahora, cuando configure la deencoding para objetos administrados, deberá pasar el objeto de contexto adecuado:

     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 

    Para codificar datos, deberá hacer algo similar utilizando la función de protocolo de encoding .

    CoreData es su propio marco de persistencia y, según su exhaustiva documentación, debe usar los inicializadores designados y seguir una ruta bastante específica para crear y almacenar objetos con él.

    Sin embargo, todavía puede usar Codable con él de forma limitada, de la misma manera que puede usar NSCoding .

    Una forma es descodificar un objeto (o una estructura) con cualquiera de estos protocolos y transferir sus propiedades a una nueva instancia de NSManagedObject que haya creado según los documentos de Core Data.

    Otra forma (que es muy común) es utilizar uno de los protocolos solo para un objeto no estándar que desee almacenar en las propiedades de un objeto administrado. Por “no estándar”, me refiero a todo lo que no se ajuste a los tipos de atributos estándar de Core Data como se especifica en su modelo. Por ejemplo, NSColor no se puede almacenar directamente como una propiedad de Objeto Administrado ya que no es uno de los tipos de atributos básicos que admite CD. En su lugar, puede usar NSKeyedArchiver para serializar el color en una instancia de NSData y almacenarlo como una propiedad de Datos en el Objeto Administrado. Invierta este proceso con NSKeyedUnarchiver . Eso es simplista y hay una forma mucho mejor de hacer esto con Core Data (ver Transient Attributes ) pero ilustra mi punto.

    También podría adoptar Encodable (uno de los dos protocolos que componen Codable , ¿puede adivinar el nombre del otro?) Para convertir una instancia de objeto administrado directamente a JSON para compartir, pero tendría que especificar las claves de encoding y su propia personalizada encode implementación ya que el comstackdor no auto-sintetizará con claves de encoding personalizadas. En este caso, querrá especificar solo las claves (propiedades) que desea incluir.

    Espero que esto ayude.

    Swift 4.2:

    Siguiendo la solución de Casademora,

    guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError() }

    debiera ser

    guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() } guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() } .

    Esto evita errores que Xcode reconoce falsamente como problemas de sectores de matriz.