web-dev-qa-db-fra.com

Définition d'une relation NSManagedObject dans Swift

Comment ajouter un objet à une propriété de relation dans une sous-classe NSManagedObject dans Swift?

Dans Objective-C, lorsque vous générez une sous-classe NSManagedObject dans Xcode à partir du modèle de données, il y a une extension de classe générée automatiquement qui contient des déclarations comme:

@interface MyManagedObject (CoreDataGeneratedAccessors)

     - (void)addMySubObject: (MyRelationshipObject *)value;
     - (void)addMySubObjects: (NSSet *)values;

@end

Cependant, Xcode n'a actuellement pas cette capacité de génération de classe pour les classes Swift.

Si j'essaie d'appeler des méthodes équivalentes directement sur l'objet Swift:

myObject.addSubObject(subObject)

... J'obtiens une erreur de compilation sur l'appel de méthode, car ces accesseurs générés ne sont pas visibles.

J'ai déclaré la propriété de relation comme @NSManaged, comme décrit dans la documentation.

Ou dois-je revenir aux objets Objective-C pour les modèles de données avec des relations?

34
Andrew Ebling

Oui, cela ne fonctionnera plus, Swift ne peut pas générer d'accesseurs au moment de l'exécution de cette manière, cela briserait le système de type.

Ce que vous devez faire est d'utiliser les chemins d'accès clés:

var manyRelation = myObject.valueForKeyPath("subObjects") as NSMutableSet
manyRelation.addObject(subObject)
/* (Not tested) */
32
iluvcapra

Depuis Xcode 7 et Swift 2.0 (voir note de publication # 17583057 ), vous pouvez simplement ajouter les définitions suivantes au fichier d'extension généré:

extension PersonModel {
    // This is what got generated by core data
    @NSManaged var name: String?
    @NSManaged var hairColor: NSNumber?
    @NSManaged var parents: NSSet?

    // This is what I manually added
    @NSManaged func addParentsObject(value:ParentModel)
    @NSManaged func removeParentsObject(value:ParentModel)
    @NSManaged func addParents(value:Set<ParentModel>)
    @NSManaged func removeParents(value:Set<ParentModel>)
}

Cela fonctionne parce que

L'attribut NSManaged peut être utilisé avec des méthodes ainsi que des propriétés, pour accéder à de nombreux accesseurs conformes à Key-Value-Coding générés automatiquement par Core Data.

L'ajout de cette définition vous permettra d'ajouter des éléments à vos collections. Je ne sais pas pourquoi ceux-ci ne sont pas uniquement générés automatiquement ...

46
lehn0058

Les données de base dans Objective C créent automatiquement des méthodes de définition ( 1 ):

Par défaut, Core Data crée dynamiquement des méthodes d'accesseur get et set publiques et primitives efficaces pour les propriétés modélisées (attributs et relations) des classes d'objets gérés. Cela inclut les méthodes de proxy mutable de codage clé-valeur telles que addObject: et supprime:, comme détaillé dans la documentation de mutableSetValueForKey: - les objets gérés sont en fait des proxys mutables pour toutes leurs relations à plusieurs.

Dans l'état actuel des choses avec Swift dans Xcode6-Beta2, vous devez implémenter ces accesseurs vous-même. Par exemple, si vous avez une relation to-many non ordonnée, de Way à Node, vous implémenteriez addNodesObject comme ceci:

class Way : NSManagedObject {
    @NSManaged var nodes : NSSet

    func addNodesObject(value: Node) {
        self.mutableSetValueForKey("nodes").addObject(value)
    }
}

La clé ici est que vous devez utiliser mutableSetValueForKey/mutableOrderedSetValueForKey/mutableArrayValueForKey. Sur ces ensembles/tableaux, vous pouvez appeler addObject et ils seront stockés lors de la prochaine vidange.

19
Bouke

L'expansion de la solution au-dessus des relations un à plusieurs est NSMutableSet, ce qui vous permet d'ajouter ou de supprimer directement la personne NSManagedObject aux rôles dans ce cas, une personne a un rôle et les rôles ont plusieurs personnes.

J'ai testé cette solution sous Xcode Beta-3 et cela fonctionne!

Ce code supprime le Département pour simplifier l'affichage du code un à un et un à plusieurs requis pour accéder aux rôles d'une personne et aux personnes d'un rôle.

import CoreData


@objc(Person) class Person: NSManagedObject {

    @NSManaged var name: String

    //One to One relationship in your Model
    @NSManaged var roles: Roles

}


@objc(Roles) class Roles: NSManagedObject {

    @NSManaged var role: String

    //One to Many relationship in your Model
    @NSManaged var persons: NSMutableSet

}

extension Roles {

    func addPersonsObject(value: Person) {
        self.persons.addObject(value)
    }

    func removePersonsObject(value: Person) {
        self.persons.removeObject(value)
    }

    func addPersons(values: [Person]) {
        self.persons.addObjectsFromArray(values)
    }

    func removePersons(values: [Person]) {
        for person in values as [Person] {
            self.removePersonsObject(person)
        }
    }

}
6
BadDogApps

Vous pouvez simplement utiliser un Set tapé à la place, ce qui est beaucoup plus facile. En suivant l'exemple fourni par @Nycen et @ lehn0058 dans la réponse précédente, vous pouvez simplement écrire:

extension PersonModel {
    @NSManaged var parents: Set<ParentModel>?
}

Et puis utilisez les méthodes insert et remove des Set.

6
FranMowinckel

Depuis Xcode 8 et Swift 3.0, Xcode génère désormais des accesseurs pour les relations. Par exemple, j'ai un magasin de classe NSManagedObject, qui a une relation un à plusieurs avec les éléments; j'ai appelé cette relation SellsItems. La classe générée pour Store a maintenant l'extension suivante à ajouter et à supprimer de SellsItems. L'ajout ou la suppression d'éléments à la relation est aussi simple que d'appeler ces fonctions.

// MARK: Generated accessors for sellsItems
extension Store {
    @objc(addSellsItemsObject:)
    @NSManaged public func addToSellsItems(_ value: Item)

    @objc(removeSellsItemsObject:)
    @NSManaged public func removeFromSellsItems(_ value: Item)

    @objc(addSellsItems:)
    @NSManaged public func addToSellsItems(_ values: NSSet)

    @objc(removeSellsItems:)
    @NSManaged public func removeFromSellsItems(_ values: NSSet)
}
5
Mario Hendricks

Comme il vous suffit de définir un côté d'une relation pour que les deux soient définis de nos jours, c'est particulièrement simple si vous avez une relation 1 <-> plusieurs, par exemple un objet Department a plusieurs objets Person, alors vous pouvez simplement utiliser:

aPerson.department = aDepartment

Si vous vérifiez, vous constaterez que aDepartment.people (en supposant qu'il s'agit de la relation réciproque que vous avez configurée) contiendra désormais l'objet Person 'aPerson'.

Si la relation est multiple <-> multiple, alors l'une des solutions les plus complexes ci-dessus semble nécessaire.

3
Peter