所以我开发了一个从字典(可能来自JSON)加载并填充数据库的通用方法。 它应该仅用于受信任的数据(来自安全通道),它不能处理循环引用,并且模式迁移可能会有问题...但对于像我这样的简单用例它应该没问题
在这里
- (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"}] }
请记住,在遵循CoreDataBooks示例代码时,它可能会破坏iOS数据存储指南:
https://developer.apple.com/icloud/documentation/data-storage/
我有一个应用程序拒绝将(只读)预先填充的数据库复制到文档目录 - 因为它然后被备份到iCloud - 而Apple只希望这发生在用户生成的文件中。
上述指南提供了一些解决方案,但它们大多归结为:
将数据库存储在缓存目录中,并优雅地处理操作系统清除缓存的情况 - 您将不得不重建数据库,这可能会为我们大多数人排除规则。
在DB文件上设置'不缓存属性',这有点神秘,因为它需要针对不同的OS版本进行不同的操作。
我不认为它太棘手,但请注意,您需要做一些额外的工作才能使示例代码与iCloud一起工作......
存储默认值的另一种方法是通过NSUserDefaults找到的。 (惊喜!) 它很容易。
有人建议,把它放进去 applicationDidFinishLaunching
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"];
这个答案仅适用于那些人
我为Android应用程序制作了预先填充的SQLite数据库。然后,当我制作iOS版本的应用程序时,我认为最好使用Core Data。所以我花了很长时间学习Core Data,然后重写代码以预填充数据库。学习如何在两个平台上完成每一步都需要大量的研究和反复试验。重叠程度远远低于我的预期。
最后我决定使用Android项目中的相同SQLite数据库。然后我使用FMDB包装器直接访问iOS中的数据库。好处:
虽然我不后悔学习核心数据,但如果我这样做,我可以通过坚持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项目更好地集成。
如何检查是否存在任何对象,如果没有,请创建一个包含一些数据的对象?
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]); } }