138 votes

Impossible de trouver une sous-classe spécifique de NSManagedObject

Je travaille au développement d'une application avec Core Data. Lorsque j'ai créé une instance en utilisant :

let entity = NSEntityDescription.entityForName("User", inManagedObjectContext: appDelegate.managedObjectContext)
let user = User(entity: entity, insertIntoManagedObjectContext: appDelegate.managedObjectContext)

J'ai reçu un avertissement dans le journal :

CoreData: warning: Unable to load class named 'User' for entity 'User'.  Class not found, using default NSManagedObject instead.

Comment pourrais-je le réparer ?

Et une autre question, comment puis-je définir une méthode d'instance dans la sous-classe NSManagedObject ?

Edit :

J'ai spécifié la classe de l'entité comme dans la capture d'écran suivante :

enter image description here

7 votes

Avez-vous préfixé le nom de la classe de l'entité avec le nom du module, comme indiqué dans le document Mise en œuvre des sous-classes d'objets gérés par Core Data ?

0 votes

@MartinR : Voir la mise à jour de ma question.

2 votes

La classe devrait être " YourAppName.User", voir la documentation (lien dans mon commentaire précédent).

221voto

Martin R Points 105727

Mise à jour pour Xcode 7 (final) : Il n'est plus nécessaire de faire précéder le nom du module à la classe (comme dans Xcode 6 et les premières versions bêta de Xcode 7). La documentation d'Apple Mise en œuvre des sous-classes d'objets gérés par Core Data a été mis à jour en conséquence.

L'inspecteur de modèle de données dispose maintenant de deux champs "Classe" et "Module" pour une entité :

enter image description here

Lorsque vous créez une sous-classe d'objet géré Swift pour l'entité, l'attribut "Module" est défini sur "Current Product Module", et avec ce paramètre, la création d'instances fonctionne à la fois dans l'application principale et dans les tests unitaires. la création d'instances fonctionne à la fois dans l'application principale et dans les tests unitaires. La sous-classe d'objet géré doit no être marqué par @objc(classname) (ceci a été observé dans https://stackoverflow.com/a/31288029/1187415 ).

Alternativement, vous pouvez vider le champ " Module " (il affichera " None ") et marquer les sous-classes d'objets gérés avec @objc(classname) (ceci a été observé dans https://stackoverflow.com/a/31287260/1187415 ).


Remarques : Cette réponse a été écrite à l'origine pour Xcode 6. Il y a eu quelques changements dans les différentes versions bêta de Xcode 7 en ce qui concerne cette question. concernant ce problème. Comme il s'agit d'une réponse acceptée avec de nombreux réponses acceptées avec beaucoup de votes positifs et de liens vers elle, j'ai essayé de résumer la situation pour la version finale actuelle de Xcode 7.

J'ai fait mes propres "recherches" et j'ai lu toutes les réponses à cette question et à la question similaire. CoreData : avertissement : Impossible de charger la classe nommée . Donc l'attribution va à chacun d'entre eux, même si je n'ai pas les énumère pas spécifiquement !


Réponse précédente pour Xcode 6 :

Comme le montre le document Mise en œuvre des sous-classes d'objets gérés par Core Data Pour cela, vous devez préfixer le nom de la classe de l'entité dans le champ Class de l'inspecteur des entités du modèle avec le nom de votre module, par exemple "MyFirstSwiftApp.User".

2 votes

Celui-ci fonctionne pour moi. Question : Si les données de base sont incluses dans un cadre, comment préfixer le nom de la sous-classe ManagedObject puisqu'il n'y a pas encore d'application réelle ?

9 votes

@Allan : Vous pourriez essayer le @objc(ClassName) méthode suggérée dans la réponse de Christer.

8 votes

Cela fonctionne, mais une fois que j'ai défini le nom de la classe à "APPNAME.User" dans l'inspecteur d'entités, je suis incapable de régénérer les classes du modèle : Xcode semble être confus par le préfixe, et il génère un fichier/classe avec le nom APPNAME. Est-ce que quelque chose m'échappe ?

62voto

Christer Points 481

Juste en passant, j'ai eu le même problème. Et tout ce que j'avais à faire était d'ajouter @objc(ClassName) dans mon fichier de classe.

Exemple :

@objc(Person)
class Person { }

Et cela a résolu mon problème.

1 votes

Vous avez défini le typealias (<%NomDeProjet%>.Personne -> Personne) en Objective-C de cette façon, donc cela fonctionne.

0 votes

Il semblerait qu'Apple ait oublié de le convertir entièrement en swift. Je ne sais pas pourquoi, mais cela a réglé le problème pour moi en douceur.

7 votes

Ceci est particulièrement utile si vous avez un projet avec plusieurs cibles, où chaque cible partage le modèle CoreData. Dans ces cas, il n'est pas possible de préfixer le nom de la classe avec celui de l'identifiant de la cible, car cela rendrait le modèle CD utile à une seule des cibles.

31voto

Mannix Points 131

La réponse acceptée à cette question m'a aidé à résoudre le même problème, mais j'avais une mise en garde qui, je pense, pourrait être utile à d'autres. Si le nom de votre projet (module) contient un espace, vous devez remplacer cet espace par un trait de soulignement. Par exemple :

Entité : MyEntity Class : Mon_Nom_de_l'application.MaClasse

0 votes

Exactement ce que je cherchais ! A la vôtre !

0 votes

Lorsque nous définissons le nom de la classe avec AppName.ClassName, Xcode 9 enlève le point et crée une classe sans point. Donc ceci n'est plus dans Xcode 9.*.

9voto

Ludovic Landry Points 3922

Selon que vous exécutez une application ou des tests, le problème peut être que l'application recherche <appName>.<entityName> et quand il est exécuté en tant que test, il ressemble à <appName>Tests.<entityName> . La solution que j'utilise pour le moment (Xcode 6.1) est de NE PAS remplir le champ Class dans l'interface utilisateur de CoreData, et de le faire en code à la place.

Ce code détectera si vous vous exécutez en tant que App ou Tests, utilisera le bon nom de module et mettra à jour le fichier de configuration du module. managedObjectClassName .

lazy var managedObjectModel: NSManagedObjectModel = {
    // The managed object model for the application. This property is not optional...
    let modelURL = NSBundle.mainBundle().URLForResource("Streak", withExtension: "momd")!
    let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)!

    // Check if we are running as test or not
    let environment = NSProcessInfo.processInfo().environment as [String : AnyObject]
    let isTest = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"

    // Create the module name
    let moduleName = (isTest) ? "StreakTests" : "Streak"

    // Create a new managed object model with updated entity class names
    var newEntities = [] as [NSEntityDescription]
    for (_, entity) in enumerate(managedObjectModel.entities) {
        let newEntity = entity.copy() as NSEntityDescription
        newEntity.managedObjectClassName = "\(moduleName).\(entity.name)"
        newEntities.append(newEntity)
    }
    let newManagedObjectModel = NSManagedObjectModel()
    newManagedObjectModel.entities = newEntities

    return newManagedObjectModel
}()

1 votes

J'ai essayé votre solution mais j'obtiens "fatal error : NSArray element failed to match the Swift Array Element type" lors du casting de NSManagedObject vers sa classe réelle. En fait, ce n'est pas lors du casting lui-même, mais lorsque j'essaie d'entrer dans une boucle for.

1 votes

Cet exemple ne fonctionne pas si vous avez une sorte d'héritage dans votre modèle. Pour chaque nouvelle entité, vous devez également itérer sur les sous-entités et les mettre à jour avec les nouvelles entités que vous avez créées.

8voto

Rien Points 26

Si vous utilisez un trait d'union dans le nom de votre projet comme "MonApp", utilisez un trait de soulignement à la place du trait d'union comme "Mon_App.MonManagedObject". En général, regardez le nom du fichier xcdatamodeld et utilisez le même préfixe que dans ce nom. Par exemple, "Mon_App_1.xcdatamodeld" nécessite le préfixe "Mon_App_1".

1 votes

Ouah. Merci, merci, merci, merci. Je serais intéressé de savoir où se trouve cette petite pépite dans la documentation d'Apple :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X