Comment utiliser swift 4 Codable dans les données de base?
Codable semble une caractéristique très excitante. Mais je me demande comment nous pouvons l'utiliser dans les données de base? En particulier, est-il possible d'encoder/décoder directement un JSON depuis/vers un NSManagedObject?
j'ai essayé un exemple très simple:
et défini Foo
moi:
import CoreData
@objc(Foo)
public class Foo: NSManagedObject, Codable {}
Mais lorsque vous l'utilisez comme ceci:
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)
le compilateur a échoué avec cette erreur:
super.init isn't called on all paths before returning from initializer
et la cible fichier est le fichier qui défini Foo
je suppose que je l'ai probablement mal fait, puisque je n'ai même pas réussi un NSManagedObjectContext
, mais je ne sais pas où le coller.
Base de Données de soutien <!--6?
3 réponses
vous pouvez utiliser l'interface Codable avec les objets CoreData pour encoder et décoder les données, mais elle n'est pas aussi automatique que lorsqu'elle est utilisée avec de vieux objets swift. Voici comment vous pouvez implémenter le décodage JSON directement avec les objets Core Data:
tout d'abord, vous rendez votre objet implémenter Codable. Cette interface doit être définie sur l'objet, et non dans une extension. Vous pouvez également définir vos Clés de Codage dans cette classe.
class MyManagedObject: NSManagedObject, Codable {
@NSManaged var property: String?
enum CodingKeys: String, CodingKey {
case property = "json_key"
}
}
Ensuite, vous pouvez définir la méthode init. Ce doit également être défini dans la méthode de classe parce que la méthode init est requise par le protocole décodable.
required convenience init(from decoder: Decoder) throws {
}
cependant, le bon initialiseur à utiliser avec les objets gérés est:
NSManagedObject.init(entity: NSEntityDescription, into context: NSManagedObjectContext)
Donc, le secret ici est d'utiliser le userInfo dictionnaire de passer dans le contexte de l'objet dans l'initialiseur. Pour ce faire, vous aurez besoin d'étendre l' CodingUserInfoKey
struct avec une nouvelle clé:
extension CodingUserInfoKey {
static let context = CodingUserInfoKey(rawValue: "context")
}
Maintenant, vous pouvez tout comme le décodeur pour le contexte:
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)
}
Maintenant, lorsque vous configurez le décodage pour les objets gérés, vous devez transmettre l'objet de contexte approprié:
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
pour encoder des données, vous aurez besoin de faire quelque chose de similaire en utilisant le encodage fonction de protocole.
CoreData est son propre cadre de persistance et, selon sa documentation complète, vous devez utiliser ses initialisateurs désignés et suivre un chemin plutôt spécifique pour créer et stocker des objets avec elle.
Vous pouvez toujours utiliser Codable
avec des moyens limités, tout comme vous pouvez utiliser NSCoding
, cependant.
une façon est de décoder un objet (ou une structure) avec l'un ou l'autre de ces protocoles et transférer ses propriétés dans un nouveau NSManagedObject
instance que vous avez créée par données de base doc.
une autre façon (qui est très courante) est d'utiliser un des protocoles seulement pour un objet non standard que vous voulez stocker dans les propriétés d'un objet géré. Par "non standard", je veux dire tout ce qui n'est pas conforme aux types d'attribut standard des données de base comme spécifié dans votre modèle. Par exemple, NSColor
ne peut pas être stocké directement en tant que propriété D'un objet géré car ce N'est pas un des types d'attributs de base pris en charge par le CD. Au lieu de cela, vous pouvez utiliser NSKeyedArchiver
pour sérialiser la couleur dans un NSData
instance et le stocker comme propriété de données dans l'objet géré. Inversez ce processus avec NSKeyedUnarchiver
. C'est simpliste et il y a une bien meilleure façon de le faire avec les données de base (voir Attributs Transitoires) mais cela illustre mon point de vue.
vous pourriez aussi peut-être adopter Encodable
(l'un des deux protocoles qui composent Codable
- pouvez-vous deviner le nom de l'autre?) pour convertir une instance D'objet géré directement en JSON pour le partage mais vous devez spécifier les clés de codage et votre propre custom encode
implémentation puisqu'il ne sera pas auto-synthétisé par le compilateur avec des clés de codage personnalisées. Dans ce cas, vous voulez spécifier les clés (propriétés) que vous voulez inclure.
J'espère que cela vous aidera.
Swift 4.2:
suivant la solution de casademora,
guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError() }
doit être
guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() }
.
ceci empêche les erreurs que Xcode reconnaît faussement comme des problèmes de tranches de tableau.
Edit: utiliser implicitement les options non emballées pour supprimer le besoin de forcer unlrap .context
à chaque fois qu'il est utilisé.