有什么方法可以预先填充核心数据?

Tan*_*ner 62 iphone core-data uitableview persist ios

我一直在创建一个列表应用程序并用核心数据支持它.

我想有一个默认的10个机场项目清单,这样用户就不必从头开始了.

有没有办法做到这一点?

任何帮助表示赞赏.提前致谢.

Ken*_*agh 58

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

然后,像在CoreDataBooks示例中一样加载该存储.

  • 如果Apple决定在iOs版本之间更改核心数据的内部(并且您没有及时发布更新),这不会打破您的应用程序吗? (6认同)
  • FWIW,我在 2014 年为一个应用程序做了同样的事情,当时创建的 sqlite 文件仍然可以在 2022 年 Xcode 中完美运行。 (2认同)

Osc*_*mez 30

是的,事实上CoreDataBooks的例子就是这样做的,你可以在这里下载代码:示例代码

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

成功保存上下文后,您可以停止应用程序,然后转到查找程序并转到文件夹:~/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;
}
Run Code Online (Sandbox Code Playgroud)

希望有所帮助.

-Oscar

  • 这个答案有误导性.您不能只将数据转储到任何旧的SQLite数据库中,然后将其加载到Core Data中.Core Data对其SQLite数据库有一个非常具体的内部结构,没有记录,建议您不要手动写入. (2认同)

git*_*rik 11

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

我使用我解析为对象的JSON文件,然后将它们插入到Core Data中.我在应用初始化时执行此操作.我还在我的核心数据中创建一个实体,指示是否已插入此初始数据,在插入初始数据后,我设置此实体,以便下次运行脚本时,看到初始数据已初始化.

要将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();
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样为它创建实体对象:

[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

}];
Run Code Online (Sandbox Code Playgroud)

如果您想要有关如何执行此操作的更详细说明,请查看本教程:http: //www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated


Mas*_*aro 8

对于10个项目,您可以applicationDidFinishLaunching:在您的应用程序委托中执行此操作.

例如insertPredefinedObjects,定义一种方法,用于创建和填充负责管理机场项目的实体实例,并保存您的上下文.您可以从文件中读取属性,也可以在代码中将它们硬连线.然后,在里面调用这个方法applicationDidFinishLaunching:.


小智 5

请记住,遵循CoreDataBooks示例代码时,它可能会违反iOS数据存储准则:

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

我有一个应用程序因将(只读)预先填充的数据库复制到文档目录而被拒绝-然后将其备份到iCloud-苹果只希望这种情况发生在用户生成的文件中。

上面的指南提供了一些解决方案,但是它们主要可以归结为:

  • 将数据库存储在缓存目录中,并妥善处理操作系统清除缓存的情况-您将不得不重建数据库,这可能对我们大多数人来说都是排除的。

  • 在数据库文件上设置“请勿缓存属性”,这有点不可思议,因为对于不同的OS版本,需要以不同的方式进行操作。

我认为这不太棘手,但是请注意,要使示例代码与iCloud一起工作还需要做更多的工作...


Sur*_*gch 5

此答案仅适用于以下人群

  • 在您的应用程序中包含预先填充的数据库
  • 制作适用于多个平台(iOS、Android 等)的应用程序

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

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

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

虽然我并不后悔学习 Core Data,但如果我重新学习,我可以通过坚持使用 SQLite 来节省大量时间。

如果您从 iOS 开始,然后计划迁移到 Android,我仍然会使用 SQLite 包装器(例如 FMDB)或其他一些软件来预填充数据库。尽管您可以从技术上提取使用 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)
Run Code Online (Sandbox Code Playgroud)

这样您就不会拥有两份副本。

更新

我现在使用SQLite.swift而不是 FMDB,因为它与 Swift 项目集成得更好。


Gau*_*noi 5

这对我有用。这是Andrea Toso对此答案的修改,并受到此博客的启发。答案的唯一问题是,使用 FileManager 移动 sqlite 文件时可能会丢失数据。我使用 ReplacePersistentStore 而不是 FileManager.default.copyItem 保存了大约 500 行数据

步骤 1
在另一个应用程序中填充您的核心数据,并使用以下代码获取文件路径:

let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
print(documentsDirectory)
Run Code Online (Sandbox Code Playgroud)

Step2
将 3 个扩展名为 .sqlite 的文件拖到 xCode 项目中。(请务必选择“添加到目标”选项)。

Step3
在 AppDelegate.swift 中创建函数来检查应用程序的首次运行

func isFirstLaunch() -> Bool {
    let hasBeenLaunchedBeforeFlag = "hasBeenLaunchedBeforeFlag"
    let isFirstLaunch = !UserDefaults.standard.bool(forKey: hasBeenLaunchedBeforeFlag)
    if (isFirstLaunch) {
        UserDefaults.standard.set(true, forKey: hasBeenLaunchedBeforeFlag)
        UserDefaults.standard.synchronize()
    }
    return isFirstLaunch
}
Run Code Online (Sandbox Code Playgroud)

步骤4
在 AppDelegate.swift 中复制此函数以获取应将 sqlite 数据库移动到的 url:

func getDocumentsDirectory()-> URL {
    let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
    let documentsDirectory = paths[0]
    return documentsDirectory
}
Run Code Online (Sandbox Code Playgroud)

第 5 步
将 persistedContainer 的声明替换为以下声明:

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "ProjectName")

    let storeUrl = self.getDocumentsDirectory().appendingPathComponent("FileName.sqlite")

    if isFirstLaunch() {
        let seededDataUrl = Bundle.main.url(forResource: "FileName", withExtension: "sqlite")
        try! container.persistentStoreCoordinator.replacePersistentStore(at: storeUrl, destinationOptions: nil, withPersistentStoreFrom: seededDataUrl!, sourceOptions: nil, ofType: NSSQLiteStoreType)
    }

    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()
Run Code Online (Sandbox Code Playgroud)