web-dev-qa-db-fra.com

Un moyen de pré-peupler les données de base?

J'ai créé une application de liste et l'ai sauvegardée avec des données de base.

J'aimerais avoir une liste par défaut d'éléments de 10 aéroports, par exemple, pour que l'utilisateur ne soit pas obligé de partir de zéro.

Y a-t-il un moyen de faire ça? 

Toute aide est appréciée. Merci d'avance.

60
Tanner

Voici le meilleur moyen (et ne nécessite pas de connaissances en SQL):
Créez une application Core Data pour iPhone rapide (ou même une application Mac) en utilisant le même modèle d’objet que votre application List. Écrivez quelques lignes de code pour enregistrer les objets gérés par défaut de votre choix dans le magasin. Ensuite, exécutez cette application dans le simulateur. Maintenant, allez dans ~/Library/Application Support/iPhone Simulator/User/Applications. Recherchez votre application parmi les GUID, puis copiez simplement le magasin sqlite dans le dossier de projet de votre application List.

Ensuite, chargez ce magasin comme dans l’exemple de CoreDataBooks.

58
Ken Aspeslagh

Oui, l’exemple de CoreDataBooks fait cela, vous pouvez télécharger le code ici: exemple de code

Ce que vous faites est de créer le magasin interne (base de données) en utilisant la procédure normale pour initialiser votre magasin, comme vous le feriez avec n'importe quel autre magasin. Ensuite, vous exécutez votre code et le laissez exécuter le code comme décrit dans l'exemple CoreDataBooks (extrait de code ci-dessous). ). Une fois le magasin initialisé, vous voudrez créer une NSManagedObjectContext et l'initialiser avec le magasin persistant créé, insérer toutes les entités dont vous avez besoin et enregistrer le contexte. 

Une fois le contexte sauvegardé, vous pouvez arrêter votre application, puis aller dans le Finder, puis dans le dossier: ~/Library/Developer tapez la recherche .sqlite et regardez sous/Developer, le tri par date vous donnera la base de données .sqlite la plus récente qui devrait correspond à l'heure d'exécution du code, vous pouvez alors prendre ce magasin et l'ajouter en tant que ressource de votre projet. Ce fichier peut ensuite être lu par un coordinateur de magasin persistant.

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

if (persistentStoreCoordinator) {
    return persistentStoreCoordinator;
}


NSString *storePath = [[self applicationDocumentsDirectory]      stringByAppendingPathComponent: @"CoreDataBooks.sqlite"];
 /*
  Set up the store.
 For the sake of illustration, provide a pre-populated default store.
 */
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
  NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"CoreDataBooks"      ofType:@"sqlite"];
 if (defaultStorePath) {
 [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
 }
}

NSURL *storeUrl = [NSURL fileURLWithPath:storePath];

 NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber   numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; 
  persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];

 NSError *error;
 if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
  // Update to handle the error appropriately.
  NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
 exit(-1);  // Fail
}    

return persistentStoreCoordinator;
}

J'espère que cela pourra aider.

-Oscar

30
Oscar Gomez

Avec cette méthode, vous n'avez pas besoin de créer une application séparée ni de connaissances en SQL. Vous devez uniquement pouvoir créer un fichier JSON pour vos données initiales.

J'utilise un fichier JSON que j'analyse dans des objets, puis je les insère dans Core Data. Je fais cela lorsque l'application s'initialise. Je crée également une entité dans mes données de base qui indique si ces données initiales sont déjà insérées. Après avoir inséré les données initiales, j'ai défini cette entité pour que le script s'exécute à la prochaine exécution et que les données initiales ont déjà été initialisées.

Pour lire le fichier json en objets:

NSString *initialDataFile = [[NSBundle mainBundle] pathForResource:@"InitialData" ofType:@"json"];
NSError *readJsonError = nil;
NSArray *initialData = [NSJSONSerialization
                        JSONObjectWithData:[NSData dataWithContentsOfFile:initialDataFile]
                        options:kNilOptions
                        error:&readJsonError];

if(!initialData) {
    NSLog(@"Could not read JSON file: %@", readJsonError);
    abort();
}

Ensuite, vous pouvez créer des objets d'entité comme ceci:

[initialData enumerateObjectsUsingBlock:^(id objData, NSUInteger idx, BOOL *stop) {

    MyEntityObject *obj = [NSEntityDescription
                          insertNewObjectForEntityForName:@"MyEntity"
                          inManagedObjectContext:dataController.managedObjectContext];

    obj.name = [objData objectForKey:@"name"];
    obj.description = [objData objectForKey:@"description"];

    // then insert 'obj' into Core Data

}];

Si vous souhaitez une description plus détaillée de la procédure à suivre, consultez le didacticiel suivant: http://www.raywenderlich.com/12170/core-data-tutorial-how-troppreloadimport-existing-data -mis à jour

11
gitaarik

Pour 10 éléments, vous pouvez simplement le faire dans applicationDidFinishLaunching: dans le délégué de votre application.

Définissez une méthode, par exemple insertPredefinedObjects, qui crée et remplit les instances de l'entité en charge de la gestion de vos éléments aéroport, et enregistrez votre contexte. Vous pouvez soit lire les attributs d’un fichier, soit simplement les connecter à votre code. Ensuite, appelez cette méthode dans applicationDidFinishLaunching:.

8
Massimo Cafaro

Lorsque vous suivez l'exemple de code CoreDataBooks, n'oubliez pas qu'il enfreint les directives de stockage de données iOS:

https://developer.Apple.com/icloud/documentation/data-storage/

Une application a été refusée pour la copie de la base de données prédéfinie (en lecture seule) dans le répertoire de documents, car elle est ensuite sauvegardée sur iCloud, et Apple souhaite uniquement que cela se produise pour les fichiers générés par l'utilisateur.

Les directives ci-dessus proposent des solutions, mais elles se résument principalement à:

  • stockez la base de données dans le répertoire des caches et gérez avec élégance les situations où le système d'exploitation les purge - vous devrez reconstruire la base de données, ce qui l'exclut probablement pour la plupart d'entre nous.

  • définissez un attribut 'ne pas mettre en cache' sur le fichier de base de données, ce qui est un peu mystérieux, car cela doit être fait différemment pour différentes versions du système d'exploitation.

Je ne pense pas que ce soit trop compliqué, mais sachez que vous avez un peu plus à faire pour que cet exemple de code fonctionne avec iCloud ...

4
Adrian Bigland

J'ai donc développé une méthode générique qui se charge à partir d'un dictionnaire (éventuellement de JSON) et remplit la base de données. Il devrait être utilisé UNIQUEMENT avec des données sécurisées (depuis un canal sécurisé), il ne peut pas gérer les références circulaires et les migrations de schéma peuvent poser problème ...

Le voici

- (void)populateDBWithDict:(NSDictionary*)dict
               withContext:(NSManagedObjectContext*)context
{
    for (NSString* entitieName in dict) {

        for (NSDictionary* objDict in dict[entitieName]) {

            NSManagedObject* obj = [NSEntityDescription insertNewObjectForEntityForName:entitieName inManagedObjectContext:context];
            for (NSString* fieldName in objDict) {

                NSString* attName, *relatedClass, *relatedClassKey;

                if ([fieldName rangeOfString:@">"].location == NSNotFound) {
                    //Normal attribute
                    attName = fieldName; relatedClass=nil; relatedClassKey=nil;
                } else {
                    NSArray* strComponents = [fieldName componentsSeparatedByString:@">"];
                    attName = (NSString*)strComponents[0];
                    relatedClass = (NSString*)strComponents[1];
                    relatedClassKey = (NSString*)strComponents[2];
                }
                SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@:", attName ]);
                NSMethodSignature* signature = [obj methodSignatureForSelector:selector];
                NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
                [invocation setTarget:obj];
                [invocation setSelector:selector];

                //Lets set the argument
                if (relatedClass) {
                    //It is a relationship
                    //Fetch the object
                    NSFetchRequest* query = [NSFetchRequest fetchRequestWithEntityName:relatedClass];
                    query.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:relatedClassKey ascending:YES]];
                    query.predicate = [NSPredicate predicateWithFormat:@"%K = %@", relatedClassKey, objDict[fieldName]];

                    NSError* error = nil;
                    NSArray* matches = [context executeFetchRequest:query error:&error];


                    if ([matches count] == 1) {
                        NSManagedObject* relatedObject = [matches lastObject];
                        [invocation setArgument:&relatedObject atIndex:2];
                    } else {
                        NSLog(@"Error! %@ = %@ (count: %d)", relatedClassKey,objDict[fieldName],[matches count]);
                    }


                } else if ([objDict[fieldName] isKindOfClass:[NSString class]]) {

                    //It is NSString
                    NSString* argument = objDict[fieldName];
                    [invocation setArgument:&argument atIndex:2];
                } else if ([objDict[fieldName] isKindOfClass:[NSNumber class]]) {

                    //It is NSNumber, get the type
                    NSNumber* argument = objDict[fieldName];
                    [invocation setArgument:&argument atIndex:2];

                }
                [invocation invoke];


            }

            NSError *error;
            if (![context save:&error]) {
                NSLog(@"%@",[error description]);
            }
        }
    }   
}

Et des tas de json ...

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"initialDB" ofType:@"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];

NSError* error;
NSDictionary *initialDBDict = [NSJSONSerialization JSONObjectWithData:jsonData
                                                           options:NSJSONReadingMutableContainers error:&error];

[ self populateDBWithDict:initialDBDict withContext: [self managedObjectContext]];

Exemples JSON

    {
    "EntitieA": [ {"Att1": 1 }, {"Att1": 2} ],
    "EntitieB": [ {"Easy":"AS ABC", "Aref>EntitieA>Att1": 1} ]
    }

et

{
    "Country": [{"Code": 55, "Name": "Brasil","Acronym": "BR"}],
    "Region": [{"Country>Country>code": 55, "Code": 11, "Name": "Sao Paulo"},
               {"Country>Country>code": 55, "Code": 31, "Name": "Belo Horizonte"}]
}
2
Felizardo

Pourquoi ne pas vérifier si des objets existent et si non, en créer un avec des données?

NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Settings"];
_managedObjectSettings = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];

if ([_managedObjectSettings count] == 0) {
    // first time, create some defaults
    NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:@"Settings" inManagedObjectContext:managedObjectContext];

    [newDevice setValue:[NSNumber numberWithBool: YES ] forKey:@"speed"];
    [newDevice setValue:[NSNumber numberWithBool: YES ] forKey:@"sound"];
    [newDevice setValue:[NSNumber numberWithBool: NO ] forKey:@"aspect"];
    [newDevice setValue:[NSNumber numberWithBool: NO  ] forKey: @"useH264"];
    [newDevice setValue:[NSNumber numberWithBool: NO ] forKey: @"useThumbnail"];

    NSError *error = nil;
    // Save the object to persistent store
    if (![managedObjectContext save:&error]) {
        NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
    }
}
2
Md1079

Cette réponse est seulement pour les personnes qui sont

  • inclure une base de données pré-remplie dans votre application
  • faire une application pour plusieurs plateformes (iOS, Android, etc.)

J'avais créé une base de données SQLite pré-remplie pour une application Android. Ensuite, lorsque je réalisais une version iOS de l'application, j'ai pensé qu'il serait préférable d'utiliser Core Data. J'ai donc passé pas mal de temps à apprendre Core Data, puis à réécrire le code pour pré-remplir la base de données. Apprendre à réaliser chaque étape des deux plates-formes a nécessité de nombreuses recherches et essais et erreurs. Il y avait beaucoup moins de chevauchement que je l'aurais espéré. 

En fin de compte, j'ai juste décidé d'utiliser la même base de données SQLite à partir de mon projet Android. Ensuite, j'ai utilisé l'encapsuleur FMDB pour accéder directement à la base de données sous iOS. Les avantages:

  • Seulement besoin de faire la base de données pré-rempli une fois.
  • Ne nécessite pas de changement de paradigme. La syntaxe entre Android et FMDB, bien que différente, reste assez similaire.
  • Avoir beaucoup plus de contrôle sur la façon dont les requêtes sont effectuées.
  • Permet la recherche en texte intégral.

Bien que je ne regrette pas d'apprendre les données de base, si je le faisais plus tard, j'aurais pu gagner beaucoup de temps en restant fidèle à SQLite.

Si vous démarrez sous iOS et que vous envisagez de passer à Android, j'utiliserais quand même un wrapper SQLite comme FMDB ou un autre logiciel pour pré-remplir la base de données. Bien que vous puissiez techniquement extraire la base de données SQLite que vous avez pré-remplie avec Core Data, le schéma (noms de table et de colonne, etc.) sera étrangement nommé.

À propos, si vous n'avez pas besoin de modifier votre base de données préremplie, ne la copiez pas dans le répertoire de documents une fois l'application installée. Il suffit d’y accéder directement à partir du paquet. 

// get url reference to databaseName.sqlite in the bundle
let databaseURL: NSURL = NSBundle.mainBundle().URLForResource("databaseName", withExtension: "sqlite")!

// convert the url to a path so that FMDB can use it
let database = FMDatabase(path: databaseURL.path)

Cela fait en sorte que vous n'avez pas deux copies.

Mettre à jour

J'utilise maintenant SQLite.Swift plutôt que FMDB, car il s'intègre mieux aux projets Swift.

2
Suragch

Une autre méthode de stockage des valeurs par défaut consiste à utiliser NSUserDefaults. (surprise!) Et c'est facile.

Suggéré par certains, mettez cela dans la applicationDidFinishLaunching

Dans le cas donné de 10 valeurs par défaut, Airport0 à 9

Réglage

NSUserDefaults *nud = [NSUserDefaults standardUserDefaults];
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport0"];
    ...
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport9"];
[nud synchronize];

ou

[[NSUserDefaults standardUserDefaults] setString:@"MACADDRESSORWHY" forKey:@"Airport9"]];
     ...
[[NSUserDefaults standardUserDefaults] synchronize];

Et puis, obtenir les valeurs par défaut.

NSString *air0 = [[NSUserDefaults standardUserDefaults] stringForKey:@"Airport0"];
0
Gabe Rainbow