web-dev-qa-db-de.com

Irgendeine Möglichkeit, Kerndaten vorab zu füllen?

Ich habe eine Listen-App erstellt und mit Kerndaten gesichert.

Ich möchte eine Standardliste mit etwa 10 Flughafenartikeln, damit der Benutzer nicht bei Null anfangen muss.

Gibt es eine Möglichkeit, dies zu tun? 

Jede Hilfe wird geschätzt. Danke im Voraus.

60
Tanner

Hier ist der beste Weg (und erfordert keine SQL-Kenntnisse):
Erstellen Sie eine schnelle Core Data iPhone-App (oder sogar eine Mac-App), die dasselbe Objektmodell wie Ihre Listen-App verwendet. Schreiben Sie einige Codezeilen, um die standardmäßig verwalteten Objekte im Geschäft zu speichern. Führen Sie dann diese App im Simulator aus. Gehen Sie jetzt zu ~/Library/Application Support/iPhone Simulator/User/Applications. Suchen Sie Ihre Anwendung unter den GUIDs, und kopieren Sie den sqlite-Store einfach in den Projektordner der List-App.

Laden Sie dann diesen Speicher wie im CoreDataBooks-Beispiel.

58
Ken Aspeslagh

Ja, das CoreDataBooks-Beispiel führt dies tatsächlich aus. Sie können den Code hier herunterladen: Beispielcode

Sie erstellen den internen Speicher (Datenbank) mithilfe der normalen Prozedur zum Initialisieren Ihres Speichers wie bei jedem anderen Speicher. Dann führen Sie einfach Ihren Code aus und lassen ihn den Code ausführen, wie im CoreDataBooks-Beispiel beschrieben (Code-Snippet unten) ). Nach dem Initialisieren des Speichers möchten Sie eine NSManagedObjectContext erstellen und mit dem erstellten persistenten Speicher initialisieren, alle erforderlichen Entitäten einfügen und den Kontext speichern. 

Nachdem der Kontext erfolgreich gespeichert wurde, können Sie die Anwendung anhalten, dann zum Finder gehen und zum folgenden Ordner wechseln: ~/Library/Developer Geben Sie in die Suche .sqlite ein und suchen Sie unter/Developer. Durch Sortieren nach Datum erhalten Sie die neueste .sqlite-Datenbank, die angezeigt werden soll Wenn Sie mit der Zeit übereinstimmen, zu der der Code ausgeführt wurde, können Sie diesen Speicher verwenden und als Ressource Ihres Projekts hinzufügen. Diese Datei kann dann von einem permanenten Speicherkoordinator gelesen werden.

- (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;
}

Hoffentlich hilft das.

-Oscar

30
Oscar Gomez

Mit dieser Methode müssen Sie keine separate App erstellen oder über SQL-Kenntnisse verfügen. Sie müssen nur eine JSON-Datei für Ihre ursprünglichen Daten erstellen können.

Ich verwende eine JSON-Datei, die ich in Objekte parse, und füge sie dann in Core Data ein. Ich mache das, wenn die App initialisiert wird. Ich mache auch eine Entität in meinen Kerndaten, die angibt, ob diese Anfangsdaten bereits eingefügt sind. Nachdem ich die ursprünglichen Daten eingefügt habe, lege ich diese Entität so fest, dass bei der nächsten Ausführung des Skripts festgestellt wird, dass die Anfangsdaten bereits initialisiert wurden.

Json-Datei in Objekte einlesen:

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();
}

Dann können Sie Entitätsobjekte wie folgt erstellen:

[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

}];

Wenn Sie eine detailliertere Beschreibung dazu wünschen, lesen Sie dieses Tutorial: http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data -aktualisierte

11
gitaarik

Für 10 Elemente können Sie dies innerhalb von applicationDidFinishLaunching: in Ihrem App-Delegierten tun.

Definieren Sie eine Methode, beispielsweise insertPredefinedObjects, die die Instanzen der für die Verwaltung Ihrer Flughafenobjekte zuständigen Entität erstellt und auffüllt, und speichern Sie den Kontext. Sie können die Attribute entweder aus einer Datei lesen oder einfach in Ihrem Code festverdrahten. Rufen Sie dann diese Methode in applicationDidFinishLaunching: auf.

8
Massimo Cafaro

Beachten Sie, wenn Sie dem Beispielcode von CoreDataBooks folgen, dass möglicherweise die iOS-Richtlinien zur Datenspeicherung verletzt werden:

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

Ich habe eine App abgelehnt, weil sie die (schreibgeschützte) vorgefüllte Datenbank in das Dokumentenverzeichnis kopiert hat, da sie dann in iCloud gesichert wird.

Die oben genannten Richtlinien bieten einige Lösungen, die sich jedoch im Wesentlichen auf Folgendes beziehen:

  • speichern Sie die Datenbank im Cache-Verzeichnis, und behandeln Sie die Situationen, in denen das Betriebssystem die Cache-Speicher löscht, auf elegante Weise. Sie müssen die Datenbank neu erstellen, was für die meisten von uns wahrscheinlich ausgeschlossen ist.

  • setzen Sie ein 'Nicht-Cache-Attribut' für die DB-Datei, was ein wenig arkan ist, da dies für verschiedene Betriebssystemversionen anders erfolgen muss.

Ich halte das nicht für zu kniffelig, aber Sie müssen wissen, dass Sie ein bisschen mehr zu tun haben, damit der Beispielcode neben iCloud funktioniert ...

4
Adrian Bigland

Daher habe ich eine generische Methode entwickelt, die aus einem Wörterbuch (möglicherweise aus JSON) lädt und die Datenbank auffüllt. Es sollte NUR mit vertrauenswürdigen Daten (von einem sicheren Kanal) verwendet werden, es kann keine Zirkelverweise verarbeiten, und Schemamigrationen können problematisch sein

Hier kommt's

- (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]);
            }
        }
    }   
}

Und lädt von 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]];

JSON-Beispiele

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

und

{
    "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

Wie wäre es, wenn Sie prüfen, ob Objekte vorhanden sind, und wenn nicht, erstellen Sie eines mit Daten.

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

Diese Antwort ist nur für Leute, die dies tun

  • eine bereits aufgefüllte Datenbank in Ihre App aufnehmen
  • erstellen einer App für mehrere Plattformen (iOS, Android usw.)

Ich hatte eine vorgefüllte SQLite-Datenbank für eine Android-App erstellt. Als ich dann eine iOS-Version der App erstellte, dachte ich, es wäre am besten, Core Data zu verwenden. Ich habe also ziemlich lange Zeit damit verbracht, Core Data zu lernen und dann den Code umzuschreiben, um die Datenbank aufzufüllen. Um zu lernen, wie Sie jeden einzelnen Schritt auf beiden Plattformen ausführen, ist viel Forschung und Versuch und Irrtum erforderlich. Es gab viel weniger Überschneidungen als ich gehofft hatte. 

Am Ende habe ich mich entschieden, die gleiche SQLite-Datenbank aus meinem Android-Projekt zu verwenden. Dann habe ich den FMDB-Wrapper verwendet, um unter iOS direkt auf die Datenbank zuzugreifen. Die Vorteile:

  • Sie müssen die vorab aufgefüllte Datenbank nur einmal erstellen.
  • Erfordert keinen Paradigmenwechsel. Die Syntax zwischen Android und FMDB ist zwar unterschiedlich, aber immer noch recht ähnlich.
  • Sie haben viel mehr Kontrolle darüber, wie Abfragen ausgeführt werden.
  • Ermöglicht die Volltextsuche.

Obwohl ich es nicht bereue, Core Data gelernt zu haben, hätte ich viel Zeit sparen können, wenn ich nur an SQLite gebunden wäre.

Wenn Sie mit iOS beginnen und dann planen, auf Android zu wechseln, würde ich immer noch einen SQLite-Wrapper wie FMDB oder eine andere Software verwenden, um die Datenbank vorab aufzufüllen. Obwohl Sie die SQLite-Datenbank, die Sie mit Core Data vorab füllen, technisch extrahieren können, wird das Schema (Tabellen- und Spaltennamen usw.) merkwürdig benannt.

Übrigens: Wenn Sie Ihre vorab aufgefüllte Datenbank nicht ändern müssen, kopieren Sie sie nach der Installation der App nicht in das Dokumentenverzeichnis. Greifen Sie einfach direkt vom Bundle darauf zu. 

// 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)

Dies macht es so, dass Sie nicht zwei Kopien haben.

Update

Ich benutze jetzt SQLite.Swift anstelle von FMDB, da es sich besser in Swift-Projekte einfügt.

2
Suragch

Eine andere Methode zum Speichern von Standardwerten wird über NSUserDefaults gefunden. (Überraschung!) Und es ist einfach.

Vorgeschlagen von einigen, geben Sie das in die applicationDidFinishLaunching

In den angegebenen Fällen mit 10 Standardwerten, Airport0 bis 9

Rahmen

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

oder

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

Und dann die Standardeinstellungen abrufen.

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