2010-02-09 146 views
56

我一直在创建一个列表应用程序并使用核心数据进行备份。任何方式预先填充核心数据?

我想有一个10机场的项目的默认列表,以便用户不必从头开始。

有没有办法做到这一点?

任何帮助表示赞赏。 在此先感谢。

+2

这是以下问题的副本:http://stackoverflow.com/questions/928177/provide-base-data-for-core-data-application,http://stackoverflow.com/questions/978187/ default-dataset-for-core-data-based-iphone-application,http:// stackoverflow。com/questions/1264382/how-do-i-initialize-a-store-with-default-data-in-a-coredata-application – 2010-02-09 19:02:49

+11

哈哈哈,是的这是一个很受欢迎的问题,没有很好的答案。 – Ziggy 2012-06-16 00:39:50

回答

57

以下是最好的方法(并且不需要SQL知识):
使用与List应用程序相同的对象模型创建一个快速的Core Data iPhone应用程序(甚至Mac应用程序)。写几行代码来保存你想要存储的默认管理对象。然后,在模拟器中运行该应用程序。现在,转到〜/库/应用程序支持/ iPhone模拟器/用户/应用程序。在GUID中查找您的应用程序,然后将sqlite存储复制到您的List应用程序的项目文件夹中。

然后,像在CoreDataBooks示例中那样加载存储。

+6

如果Apple决定更改iOs版本之间的核心数据内部(并且您没有及时发布更新),这会不会破坏您的应用程序? – 2012-02-01 09:19:24

+0

我诚恳地怀疑苹果公司会做出一个改变,使其无法读取自己的数据库文件。 – 2012-02-03 19:54:24

+0

Apple在系统升级过程中可以迁移设备上的所有现有核心数据库,因此它仍然可以读取它们。但是这种迁移可能会在新安装中跳过预先打包的数据库文件。 – 2012-02-08 10:03:19

29

是存在事实上的CoreDataBooks例子没有这一点,你可以从这里下载代码:sample code

你要做的就是创建一个使用正常的程序来初始化你的店就像你的内部存储(数据库)与任何其他商店,然后你只需运行你的代码,并让它执行代码,如CoreDataBooks示例(下面的代码片段)中所述。商店初始化后,您需要创建一个NSManagedObjectContext并使用创建的持久性存储进行初始化,插入所需的所有实体并保存上下文。

一旦上下文被成功保存,您可以停止您的应用程序,然后进入finder并进入文件夹:~/Library/Developer键入搜索.sqlite并查看/ Developer下,按日期排序会给你最近的。 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

+0

进出口猜测生病了必须知道这个正确的SQL? – Tanner 2010-02-09 16:02:19

+0

不,你不是我创建我的就像你会创建它使用SQL Server 2005,只需插入值,使用SQLite数据库浏览器,你可以在这里:http://mac.softpedia.com/get/Developer-Tools /SQLite-Database-Browser.shtml – 2010-02-09 16:05:00

+0

我相信我会更喜欢sql浏览器的方法,因为我可以添加不同的列表。我下载了它。我只需要添加一个项目,将其命名为护照并添加9个项目,然后即时完成? – Tanner 2010-02-09 16:11:16

8

为10个项目,你可以在你的应用程序代理做内applicationDidFinishLaunching:

定义一个方法,比如说insertPredefinedObjects,它创建并填充负责管理机场物件的实体的实例,并保存您的上下文。您可以从文件中读取属性,或者直接在您的代码中硬连线。然后,在applicationDidFinishLaunching:内调用此方法。

5

请记住,继CoreDataBooks示例代码时,它可能打破了iOS的数据存储指南:

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

我已经拒绝了复制的应用程序(只读)预 - 将数据库填充到文档目录 - 随后将其备份到iCloud - 而Apple只希望这种情况发生在用户生成的文件中。

上面提供一些解决方案的指导方针,但他们大多归结为:

  • 店DB的缓存目录,并妥善处理的情况下操作系统清除缓存 - 你将不得不重建数据库,它可能排除了我们大多数人。

  • 设置数据库文件中的“不缓存属性”,这是一个有点神秘,因为它需要针对不同的操作系统版本不同的做法。

我不认为这是太棘手,但要知道,你有一些额外的做,使该示例代码一起工作的iCloud ...

0

存储默认另一种方法是找到通过NSUserDefaults。 (惊喜!) 而且很简单。

通过一些建议,它放入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"]; 
11

使用此方法,您无需制作单独的应用程序或具有任何SQL知识。你只需要能够为你的初始数据创建一个JSON文件。

我用我解析为对象的JSON文件,然后在核心数据插入。我在应用程序初始化时执行此操作。我还在核心数据中创建了一个实体,用于指示是否已插入此初始数据,插入初始数据后,我设置此实体,以便在下次脚本运行时发现初始数据已经初始化。

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

然后你可以为它的实体对象是这样的:

[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 

}]; 

如果您想如何做到这一点更详细的说明,检查out this tutorial: http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated

2

所以我开发了一个通用的方法,从字典中加载(可能来自JSON)并填充da tabase。 它应该仅用于受信任的数据(来自安全通道),它不能处理循环引用,并且模式迁移可能会有问题...但对于简单的使用情况,如我的应该没问题

- (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"}] 
} 
2

如何检查是否存在任何对象,如果没有,创建一个有一些数据?

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

这个回答的人谁是

  • 包括在你的应用程序
  • 预填充的数据库,使您对多个平台(iOS版,Android等)
的应用

我为Android应用程序制作了预先填充的SQLite数据库。然后,当我制作应用的iOS版本时,我认为最好使用Core Data。所以我花了很长时间学习Core Data,然后重写代码以预先填充数据库。学习如何在两个平台上进行每一步都需要大量的研究和试验和错误。与我希望的相比,重叠程度要少得多。

最后我只是决定从我的Android项目中使用相同的SQLite数据库。然后我使用FMDB封装来直接访问iOS中的数据库。好处:

  • 只需要预先填充一次数据库。
  • 不需要范式转换。 Android和FMDB之间的语法虽然不同,但仍然非常相似。
  • 对查询的执行方式有更多的控制权。
  • 允许全文搜索。

虽然我不后悔学习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,因为它集成了斯威夫特的项目更好。

+0

SQLite.swift很神奇。说@Suragch,你可能知道...在Android中,你有SQLiteOpenHelper,它具有方便的“OnUpgrade”概念。处理应用何时升级(即应用商店的新版本)。你碰巧知道,这个问题在iOS中是如何处理的,它会成为一个问题吗?干杯 – Fattie 2016-12-12 09:18:18

+0

顺便说一句,你上面说的是如此真实。如果你正在做ios-android开发,那么简单地坚持SQLite就好多了。即使对于以前从未使用过sql的人来说,这只是学习一些简单的sql语句的问题。 – Fattie 2016-12-12 09:20:13

+0

@JoeBlow,我还没有在Sqlite.swift中完成模式更新。我想我回想起在文档中阅读它的一些内容,但我现在只能看到[this](https://github.com/stephencelis/SQLite.swift/blob/master/Documentation/Index.md#migrations-和架构的版本)。它看起来不像“OnUpgrade”与Android一样内置。 – Suragch 2016-12-12 13:28:48