usar tutorial porque persistencia efecto datos data apple ios iphone uitableview core-data persist

ios - tutorial - usar core data in swift



¿Alguna manera de poblar previamente los datos básicos? (9)

¿Qué hay de comprobar si existen objetos y si no, crear uno con algunos datos?

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

He estado creando una aplicación de lista y la estoy respaldando con datos básicos.

Me gustaría tener una lista predeterminada de, por ejemplo, 10 elementos del aeropuerto, para que el usuario no tenga que empezar de cero.

¿Hay alguna manera de hacer esto?

Cualquier ayuda es apreciada. Gracias por adelantado.


Así que he desarrollado un método genérico que se carga desde un diccionario (posiblemente desde JSON) y rellena la base de datos. Debe usarse ÚNICAMENTE con datos confiables (de un canal seguro), no puede manejar referencias circulares y las migraciones de esquemas pueden ser problemáticas ... Pero para casos de uso simple como el mío, debería estar bien.

Aquí va

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

Y cargas 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]];

Ejemplos de JSON

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

y

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


Esta es la mejor manera (y no requiere conocimiento de SQL):
Cree una aplicación rápida de Core Data para iPhone (o incluso una aplicación de Mac) usando el mismo modelo de objeto que su aplicación de lista. Escriba unas líneas de código para guardar los objetos gestionados predeterminados que desea en la tienda. Luego, ejecute esa aplicación en el simulador. Ahora, vaya a ~ / Library / Application Support / iPhone Simulator / User / Applications. Encuentre su aplicación entre los GUID, luego simplemente copie la tienda sqlite en la carpeta de proyecto de su aplicación de lista.

Luego, cargue esa tienda como lo hacen en el ejemplo de CoreDataBooks.


Esta respuesta es solo para personas que están

  • incluyendo una base de datos pre-poblada en su aplicación
  • hacer una aplicación para múltiples plataformas (iOS, Android, etc.)

Hice una base de datos SQLite prepoblada para una aplicación de Android. Luego, cuando estaba creando una versión iOS de la aplicación, pensé que sería mejor usar Core Data. Así que dediqué bastante tiempo a aprender Core Data y luego reescribí el código para rellenar la base de datos. Aprender cómo hacer cada paso en ambas plataformas requirió mucha investigación y prueba y error. Hubo mucha menos superposición de lo que hubiera esperado.

Al final, decidí usar la misma base de datos SQLite de mi proyecto Android. Luego usé el contenedor FMDB para acceder directamente a la base de datos en iOS. Los beneficios:

  • Solo necesita hacer la base de datos prepoblada una vez.
  • No requiere un cambio de paradigma. La sintaxis entre Android y FMDB, aunque diferente, es bastante similar.
  • Tenga mucho más control sobre cómo se realizan las Consultas.
  • Permite la búsqueda de texto completo.

Aunque no me arrepiento de haber aprendido Core Data, si lo hubiera hecho podría haber ahorrado mucho tiempo solo con SQLite.

Si está comenzando en iOS y planea mudarse a Android, aún usaría un contenedor SQLite como FMDB o algún otro software para rellenar previamente la base de datos. Aunque puede extraer técnicamente la base de datos SQLite que rellena previamente con Core Data, el esquema (nombres de tabla y columna, etc.) tendrá un extraño nombre.

Por cierto, si no necesita modificar su base de datos prepoblada, no la copie en el directorio de documentos una vez instalada la aplicación. Solo acceda directamente desde el paquete.

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

Esto lo hace para que no tenga dos copias.

Actualizar

Ahora uso SQLite.swift lugar de FMDB, porque se integra mejor con los proyectos de Swift.


Otro método para almacenar los valores predeterminados se encuentra a través de NSUserDefaults. (¡sorpresa!) Y es fácil.

Sugerido por algunos, poner eso en la applicationDidFinishLaunching

En el caso dado de 10 valores predeterminados, Airport0 hasta 9

Ajuste

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

o

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

Y luego, obteniendo los valores predeterminados.

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


Para 10 elementos, puede hacer esto dentro de la applicationDidFinishLaunching: en su aplicación delegada.

Defina un método, digamos insertPredefinedObjects , que crea y rellena las instancias de la entidad a cargo de la administración de los elementos del aeropuerto y guarde su contexto. Puede leer los atributos de un archivo o simplemente conectarlos en su código. Luego, llame a este método dentro de applicationDidFinishLaunching:


Sí, de hecho, el ejemplo de CoreDataBooks hace esto, puede descargar el código aquí: código de muestra

Lo que hace es crear la tienda interna (base de datos) usando el procedimiento normal para inicializar su tienda como lo haría con cualquier otra tienda, luego simplemente ejecute su código y permita que ejecute el código como se describe en el ejemplo de CoreDataBooks (fragmento de código a continuación ) Una vez que la tienda se haya inicializado, querrá crear un NSManagedObjectContext e inicializarlo con el almacén persistente creado, insertar todas las entidades que necesite y guardar el contexto.

Una vez que el contexto se ha guardado correctamente, puede detener su aplicación, luego vaya al buscador y vaya a la carpeta: ~/Library/Developer escriba en la búsqueda .sqlite y mire en / Developer, sorting by date le dará la más reciente. base de datos sqlite que debe coincidir con la hora en que se ejecutó el código, puede tomar esta tienda y agregarla como recurso de su proyecto. Este archivo puede ser leído por un coordinador de tienda persistente.

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

Espero que ayude.

-Oscar


Tenga en cuenta que, al seguir el código de ejemplo de CoreDataBooks, probablemente rompa las pautas de almacenamiento de datos de iOS:

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

Se me ha rechazado una aplicación para copiar la base de datos (de solo lectura) previamente poblada al directorio de documentos, ya que luego se respalda en iCloud, y Apple solo quiere que eso suceda con los archivos generados por el usuario.

Las pautas anteriores ofrecen algunas soluciones, pero en su mayoría se reducen a:

  • almacene el DB en el directorio de cachés y maneje con gracia las situaciones en las que el SO purga los cachés; tendrá que reconstruir el DB, lo que probablemente lo descarte para la mayoría de nosotros.

  • establecer un ''atributo de no caché'' en el archivo DB, que es un poco arcano, ya que debe hacerse de manera diferente para las diferentes versiones del sistema operativo.

No creo que sea demasiado complicado, pero ten en cuenta que tienes un poco más que hacer para que ese código de ejemplo funcione junto con iCloud ...


Con este método, no es necesario crear una aplicación por separado o tener ningún conocimiento de SQL. Solo necesita poder hacer un archivo JSON para sus datos iniciales.

Utilizo un archivo JSON que analizo en objetos, luego los inserto en Core Data. Lo hago cuando la aplicación se inicializa. También hago una entidad en mis datos básicos que indica si estos datos iniciales ya están insertados, después de insertar los datos iniciales configuro esta entidad para que la próxima vez que se ejecute el script vea que los datos iniciales ya se hayan inicializado.

Para leer el archivo json en objetos:

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

Entonces puedes hacer objetos de entidad para esto así:

[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 desea una descripción más detallada sobre cómo hacerlo, consulte este tutorial: http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated