138 votes

Ajout de Core Data à un projet iPhone existant

J'aimerais ajouter des données de base à un projet iPhone existant, mais je reçois toujours beaucoup d'erreurs de compilation :

- NSManagedObjectContext undeclared

 - Expected specifier-qualifier-list before 'NSManagedObjectModel'

 - ...

J'ai déjà ajouté le Core Data Framework à la cible (clic droit sur mon projet sous "Targets", "Add" - "Existing Frameworks", "CoreData.framework").

Mon fichier d'en-tête :

NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;       
NSPersistentStoreCoordinator *persistentStoreCoordinator;

[...]

@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

Qu'est-ce que je rate ? Le lancement d'un nouveau projet n'est pas une option...

Merci beaucoup !

modifier Désolé, j'ai bien ces implémentations... mais il semble que la bibliothèque soit absente... les méthodes d'implémentation sont pleines d'erreurs de compilation comme " managedObjectContext undeclared ", " NSPersistentStoreCoordinator undeclared "mais aussi avec "Attendu ')' avant NSManagedObjectContext " (bien qu'il semble que les parenthèses soient correctes)...

#pragma mark -
#pragma mark Core Data stack

/**
 Returns the managed object context for the application.
 If the context doesn't already exist, it is created and bound to the persistent store         
coordinator for the application.
 */
- (NSManagedObjectContext *) managedObjectContext {

    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }
    return managedObjectContext;
}

/**
 Returns the managed object model for the application.
 If the model doesn't already exist, it is created by merging all of the models found in    
 application bundle.
 */
- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel != nil) {
        return managedObjectModel;
    }
    managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];    
    return managedObjectModel;
}

/**
 Returns the persistent store coordinator for the application.
 If the coordinator doesn't already exist, it is created and the application's store added to it.
 */
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] 
        stringByAppendingPathComponent: @"Core_Data.sqlite"]];

    NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] 
    initWithManagedObjectModel:[self managedObjectModel]];
    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
    configuration:nil URL:storeUrl options:nil error:&error]) {
    /*
     Replace this implementation with code to handle the error appropriately.

     abort() causes the application to generate a crash log and terminate. You should 
    not use this function in a shipping application, although it may be useful during 
    development. If it is not possible to recover from the error, display an alert panel that 
    instructs the user to quit the application by pressing the Home button.

     Typical reasons for an error here include:
     * The persistent store is not accessible
     * The schema for the persistent store is incompatible with current managed object 
                model
     Check the error message to determine what the actual problem was.
     */
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}    

return persistentStoreCoordinator;
}

144voto

Joost Points 7673

Tous les fichiers d'en-tête de CoreData sont importés dans App_Prefix.pch Ainsi, les classes CoreData seront disponibles dans l'ensemble de votre projet, ce qui vous évitera d'avoir à importer manuellement l'en-tête dans les fichiers dont vous avez besoin.

Ouvrez donc Xcode et cherchez un fichier du genre App_Prefix.pch par défaut, il se trouve dans le Other Sources groupe. Après le UIKit d'importation, ajoutez la ligne suivante :

#import <CoreData/CoreData.h>

Et vous devriez être prêt à partir.

Xcode 4

Pour les projets créés dans Xcode 4, le fichier de préfixe peut être trouvé dans le fichier Supporting Files dans le navigateur du projet. Il s'appelle ' nom du projet -Préfixe.pch' par défaut.

Xcode 6+ (en anglais)

À partir de Xcode 6, le fichier d'en-tête précompilé n'est plus inclus par défaut. Cela est dû à l'introduction des Modules, qui supprime la nécessité d'utiliser des en-têtes précompilés. Bien qu'il soit toujours possible d'ajouter manuellement un fichier PCH pour inclure globalement les en-têtes CoreData, envisagez de spécifier la dépendance CoreData en utilisant @import CoreData; * dans chaque fichier qui utilise CoreData. Cela rend les dépendances explicites et, plus important encore, évitera le problème de cette question à l'avenir.

* Modules <a href="http://useyourloaf.com/blog/modules-and-precompiled-headers.html" rel="nofollow noreferrer">doivent être activés </a>pour que cela fonctionne.

0 votes

Que faire si je ne trouve pas de fichier App_prefix.pch, je travaille avec xcode 6.4 et ios 8.4.

129voto

ColossalChris Points 666

Pour expliquer toutes les étapes à suivre pour ajouter des données de base à un projet qui n'en disposait pas auparavant :

Étape 1 : Ajouter le cadre

Cliquez sur la cible de votre application (dans le panneau de gauche, l'icône supérieure avec le nom de votre application), puis allez dans l'onglet "Build Phases", puis sur "Link Binary With Libraries", cliquez sur le petit "+" en bas, puis trouvez "CoreData.framework" et ajoutez-le à votre projet.

Ensuite, vous pouvez soit importer des corédonnées sur tous les objets dont vous avez besoin (la manière non sexy) en utilisant :

Swift

import CoreData

Objectif C

#import <CoreData/CoreData.h>

ou ajouter l'importation en dessous des importations communes dans votre fichier .pch (beaucoup plus sexy) comme ceci :

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import <CoreData/CoreData.h>
#endif

Étape 2 : Ajouter le modèle de données

Pour ajouter le fichier .xcdatamodel, faites un clic droit/contrôle-clic sur vos fichiers dans le volet de droite (comme dans un dossier de ressources pour plus de sécurité) et sélectionnez Ajouter un nouveau fichier, cliquez sur l'onglet Données de base lorsque vous sélectionnez votre type de fichier, puis cliquez sur Modèle de données, donnez-lui un nom et cliquez sur Suivant et Terminer pour l'ajouter à votre projet. Lorsque vous cliquez sur cet objet Modèle, vous verrez l'interface permettant d'ajouter les Entités à votre projet avec les relations que vous souhaitez.

Étape 3 : Mise à jour du délégué de l'application

En Swift sur AppDelegate.swift

//replace the previous version of applicationWillTerminate with this
func applicationWillTerminate(application: UIApplication) {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    // Saves changes in the application's managed object context before the application terminates.
    self.saveContext()
}

func saveContext () {
    var error: NSError? = nil
    let managedObjectContext = self.managedObjectContext
    if managedObjectContext != nil {
        if managedObjectContext.hasChanges && !managedObjectContext.save(&error) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            //println("Unresolved error \(error), \(error.userInfo)")
            abort()
        }
    }
}

// #pragma mark - Core Data stack

// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
var managedObjectContext: NSManagedObjectContext {
    if !_managedObjectContext {
        let coordinator = self.persistentStoreCoordinator
        if coordinator != nil {
            _managedObjectContext = NSManagedObjectContext()
            _managedObjectContext!.persistentStoreCoordinator = coordinator
        }
    }
    return _managedObjectContext!
}
var _managedObjectContext: NSManagedObjectContext? = nil

// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
var managedObjectModel: NSManagedObjectModel {
    if !_managedObjectModel {
        let modelURL = NSBundle.mainBundle().URLForResource("iOSSwiftOpenGLCamera", withExtension: "momd")
        _managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)
    }
    return _managedObjectModel!
}
var _managedObjectModel: NSManagedObjectModel? = nil

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
var persistentStoreCoordinator: NSPersistentStoreCoordinator {
    if !_persistentStoreCoordinator {
        let storeURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent("iOSSwiftOpenGLCamera.sqlite")
        var error: NSError? = nil
        _persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        if _persistentStoreCoordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error) == nil {
            /*
            Replace this implementation with code to handle the error appropriately.
            abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            Typical reasons for an error here include:
            * The persistent store is not accessible;
            * The schema for the persistent store is incompatible with current managed object model.
            Check the error message to determine what the actual problem was.
            If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
            If you encounter schema incompatibility errors during development, you can reduce their frequency by:
            * Simply deleting the existing store:
            NSFileManager.defaultManager().removeItemAtURL(storeURL, error: nil)
            * Performing automatic lightweight migration by passing the following dictionary as the options parameter:
            [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true}
            Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
            */
            //println("Unresolved error \(error), \(error.userInfo)")
            abort()
        }
    }
    return _persistentStoreCoordinator!
}
var _persistentStoreCoordinator: NSPersistentStoreCoordinator? = nil

// #pragma mark - Application's Documents directory

// Returns the URL to the application's Documents directory.
var applicationDocumentsDirectory: NSURL {
    let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
    return urls[urls.endIndex-1] as NSURL
}

En Objectif C assurez-vous d'ajouter ces objets à AppDelegate.h

 @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
 @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
 @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

 - (NSURL *)applicationDocumentsDirectory; // nice to have to reference files for core data

Synthétisez les objets précédents dans AppDelegate.m comme ceci :

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

Ajoutez ensuite ces méthodes à AppDelegate.m (veillez à mettre le nom du modèle que vous avez ajouté dans les emplacements indiqués) :

- (void)saveContext{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}

- (NSManagedObjectContext *)managedObjectContext{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}

- (NSManagedObjectModel *)managedObjectModel{
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"NAMEOFYOURMODELHERE" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"NAMEOFYOURMODELHERE.sqlite"];

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {

        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _persistentStoreCoordinator;
}

 #pragma mark - Application's Documents directory

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

Étape 4 : acheminer les objets de données vers les contrôleurs de vue où vous avez besoin des données

Option 1. Utiliser le ManagedObjectContext du délégué de l'application à partir de VC (préférable et plus facile)

Comme suggéré par @brass-kazoo - Récupérer une référence à AppDelegate et son managedObjectContext via :

Swift

 let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
 appDelegate.managedObjectContext

Objectif C

 [[[UIApplication sharedApplication] delegate] managedObjectContext];

dans votre ViewController

Option 2. Créer ManagedObjectContext dans votre VC et le faire correspondre à celui de l'AppDelegate (Original)

Ne montrer que l'ancienne version pour l'Objective C, car il est beaucoup plus facile d'utiliser la méthode préférée.

dans le ViewController.h

@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;

Dans le ViewController.m

@synthesize managedObjectContext = _managedObjectContext;

Dans l'AppDelegate, ou dans la classe où le ViewController est créé, définissez le managedObjectContext comme étant le même que celui de l'AppDelegate.

ViewController.managedObjectContext = self.managedObjectContext;

Si vous voulez que le viewcontroller utilisant Core Data soit un FetchedResultsController, vous devrez vous assurer que ces éléments se trouvent dans votre ViewController.h.

@interface ViewController : UIViewController <NSFetchedResultsControllerDelegate> {
  NSFetchedResultsController *fetchedResultsController;
  NSManagedObjectContext *managedObjectContext;
}

 @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;

Et ceci est dans ViewController.m

@synthesize fetchedResultsController, managedObjectContext;

Après tout cela, vous pouvez maintenant utiliser ce managedObjectContext pour exécuter toutes les requêtes de récupération habituelles nécessaires pour le bon fonctionnement de CoreData ! Amusez-vous bien sur

2 votes

Dans AppDelegate.h, la méthode applicationDoumentsDirectory devrait renvoyer NSURL au lieu de NSString.

9 votes

C'est la meilleure réponse IMO ! Bien qu'au lieu de l'étape 4, j'ai récupéré une référence à AppDelegate par le biais de [[UIApplication sharedApplication] delegate] et ensuite le contexte via [appDelegate managedObjectContext]

0 votes

N'oubliez pas d'importer Coredata dans votre fichier AppDelegate.h.

11voto

Eimantas Points 29052

Essayez de créer une application Cocoa soutenue par Core Data et regardez AppDelegate. Vous y trouverez des méthodes d'implémentation de la pile de données de base ainsi qu'un fichier de modèle d'objet géré pour définir vos entités et d'autres éléments liés aux données de base.

Vous nous avez montré seulement l'en-tête (c'est-à-dire la déclaration), mais pas l'implémentation (c'est-à-dire la définition) de la pile Core Data.

0 votes

Pour swift 3 j'ai utilisé les étapes de ColossalChris mais dans la partie de l'AppDelegate j'ai utilisé cette réponse (copie d'un nouveau projet avec support des données de base) pour obtenir un code compatible avec swift 3.

8voto

Jay Greene Points 71

Si vous rencontrez ce même problème dans xcode 4, comme je l'ai fait. C'est différent : je devais sélectionner le projet, puis dans targets expandre "Lier le binaire avec les bibliothèques" qui affiche les bibliothèques actuelles. À partir de là, cliquez sur le + (signe plus) pour sélectionner les bibliothèques supplémentaires dont vous avez besoin. Je l'ai placé en haut du projet et j'ai dû le déplacer (par glisser-déposer) vers la zone de stockage de la bibliothèque. Groupe des cadres mais c'est tout.

5voto

Henrik P. Hessel Points 22046

Comme Eimantas l'a dit, il vous manque l'implémentation de la pile de base, comme par exemple

- (NSManagedObjectContext *) managedObjectContext;
- (NSManagedObjectModel *)managedObjectMode;
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator;

Une solution serait de créer un nouveau projet de pilote de données de base et de copier/coller l'implémentation dans votre projet.

2 votes

Alors comment gérer le contexte serait-il conscient du modèle ? automatiquement ?

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