任何方式预先填充核心数据?
我一直在创build一个列表应用程序,并用核心数据进行备份。
我想要有一个10机场的项目的默认列表,以便用户不必从头开始。
有没有办法做到这一点?
任何帮助表示赞赏。 提前致谢。
这是最好的方法(不需要SQL知识):
使用与List应用程序相同的对象模型创build一个快速的Core Data iPhone应用程序(甚至Mac应用程序)。 写几行代码来保存你想要的商店的默认pipe理对象。 然后,在模拟器中运行该应用程序。 现在,去〜/库/应用程序支持/ iPhone模拟器/用户/应用程序。 在GUID中查找您的应用程序,然后将sqlite存储复制到您的List应用程序的项目文件夹中。
然后,像在CoreDataBooks示例中一样加载该存储。
是的,其实CoreDataBooks的例子是这样做的,你可以在这里下载代码: 示例代码
你所做的就是使用正常的程序创build内部存储(数据库)来初始化你的商店,就像你在任何其他商店一样,然后你只需运行你的代码,并让它执行代码,如CoreDataBooks示例中所述(下面的代码片段)。 一旦存储被初始化,你将需要创build一个NSManagedObjectContext
,并用创build的持久化存储对其进行初始化,插入所有你需要的实体,并保存上下文。
一旦上下文成功保存,你可以停止你的应用程序,然后去find并进入文件夹: ~/Library/Developer
键入search.sqlite,并在/ Developer下查看,按datesorting会给你最近的。 sqlite数据库应该匹配代码执行的时间,然后你可以把这个商店,并添加它作为你的项目的资源。 这个文件可以被持久性存储协调器读取。
- (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; }
希望有所帮助。
-Oscar
使用这种方法,您不需要制作单独的应用程序或具有任何SQL知识。 您只需要能够为您的初始数据创build一个JSON文件。
我使用JSON文件parsing对象,然后将它们插入到Core Data中。 我在应用程序初始化时执行此操作。 我还在我的核心数据中创build了一个实体,指出是否已经插入了这个初始数据,在插入初始数据之后,我设置了这个实体,所以下一次脚本运行时就会看到初始化的数据已经被初始化了。
将json文件读入对象:
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(); }
然后你可以像这样为它创build实体对象:
[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 }];
如果你想要更详细地描述如何做到这一点,请查看本教程: http : //www.raywenderlich.com//12170/core-data-tutorial-how-to-preloadimport-existing-data-updated
对于10个项目,您可以在applicationDidFinishLaunching:
执行此操作applicationDidFinishLaunching:
在您的应用程序委托中。
定义一个方法,例如insertPredefinedObjects
,它创build并填充pipe理机场项目的实体实例,并保存上下文。 您可以从文件读取属性,或者直接在您的代码中硬连线。 然后,在applicationDidFinishLaunching:
调用这个方法。
请记住,遵循CoreDataBooks示例代码时,可能会违反iOS数据存储指南:
https://developer.apple.com/icloud/documentation/data-storage/
我有一个应用程序被拒绝,因为它将(只读)预填充的数据库复制到文档目录 – 因为它被备份到iCloud – 而苹果只希望发生用户生成的文件。
上述指导方针提供了一些解决scheme,但主要归结为:
-
将数据库存储在caching目录中,并妥善处理操作系统清除caching的情况 – 您将不得不重build数据库,这可能会为我们大多数人解决问题。
-
在数据库文件上设置一个“不caching属性”,这有点神秘,因为不同的操作系统版本需要不同的处理。
我不认为这是太棘手,但要知道,你有一些额外的事情,使该示例代码与iCloud一起工作…
所以我开发了一个从字典(可能来自JSON)加载并填充数据库的generics方法。 它应该只用于受信任的数据(来自安全通道),它不能处理循环引用,模式迁移可能会有问题…但是对于像我这样的简单用例,应该没问题
在这里
- (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]); } } } }
从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示例
{ "EntitieA": [ {"Att1": 1 }, {"Att1": 2} ], "EntitieB": [ {"Easy":"AS ABC", "Aref>EntitieA>Att1": 1} ] }
和
{ "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"}] }
如何检查是否存在任何对象,如果没有,创build一个与一些数据?
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]); } }
这个答案只适用于那些人
- 包括应用程序中的预填充数据库
- 为多个平台(iOS,Android等)制作应用程序
我为Android应用程序创build了预填充的SQLite数据库。 然后,当我正在制作应用程序的iOS版本时,我认为最好使用Core Data。 所以我花了很长时间学习Core Data,然后重写代码来预先填充数据库。 学习如何在两个平台上进行每一步都需要大量的研究和试验和错误。 比我想象的要less得多。
最后,我决定从我的Android项目中使用相同的SQLite数据库。 然后我使用FMDB封装来直接访问iOS中的数据库。 好处:
- 只需要预先填充一次数据库。
- 不要求范式转变。 Android和FMDB之间的语法虽然不同,但仍然非常相似。
- 对查询的执行方式有更多的控制权。
- 允许全文search。
虽然我不后悔学习Core Data,但是如果我这样做的话,我可以通过坚持SQLite来节省大量的时间。
如果你是从iOS开始,然后计划转移到Android,我仍然会使用像FMDB或其他一些软件的SQLite包装预填充数据库。 虽然技术上可以提取预填充Core Data的SQLite数据库,但架构(表名和列名等)将被奇怪地命名。
顺便说一下,如果您不需要修改预填充的数据库,那么在安装应用程序之后不要将其复制到文档目录。 只需从包中直接访问它。
// 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)
这使得你没有两个副本。
更新
我现在使用SQLite.swift而不是FMDB,因为它与Swift项目集成得更好。
另一种存储默认值的方法是通过NSUserDefaults来find的。 (惊喜!)而且很简单。
通过一些build议,把这个到applicationDidFinishLaunching
在给定的10个违约情况下,Airport0到9
设置
NSUserDefaults *nud = [NSUserDefaults standardUserDefaults]; [nud setString:@"MACADDRESSORWHY" forKey:@"Airport0"]; ... [nud setString:@"MACADDRESSORWHY" forKey:@"Airport9"]; [nud synchronize];
要么
[[NSUserDefaults standardUserDefaults] setString:@"MACADDRESSORWHY" forKey:@"Airport9"]]; ... [[NSUserDefaults standardUserDefaults] synchronize];
然后,获取默认值。
NSString *air0 = [[NSUserDefaults standardUserDefaults] stringForKey:@"Airport0"];