更新核心数据存储位置以支持应用程序组

时间:2014-09-06 11:48:45

标签: core-data ios8


我在App Store中已有一个使用核心数据来保存数据的应用程序 现在,当iOS 8即将问世时,我想为它添加一个小部件,因此我必须使用App Groups在二进制文件之间共享数据。
但有一个问题 - 我需要更改商店位置以支持应用程序组到所有现有用户 我编写了以下代码,尝试将商店移至新路径:

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }

    NSURL *oldStoreURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    oldStoreURL = [oldStoreURL URLByAppendingPathComponent:@"Schooler.sqlite"];


    NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.schooler.mycontainer"];
    storeURL = [storeURL URLByAppendingPathComponent:@"Schooler.sqlite"];


    if([[NSFileManager defaultManager] fileExistsAtPath:oldStoreURL.path] == YES && [[NSFileManager defaultManager] fileExistsAtPath:storeURL.path] == NO)
    {
        // Prior today extension - Need to move to new directory
        NSError *error = nil;
        if([[NSFileManager defaultManager] moveItemAtURL:oldStoreURL toURL:storeURL error:&error] == YES)
            NSLog(@"Migrated successfully to new database location.");
        else
            NSLog(@"error: %@",error);
    }

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }

    return _persistentStoreCoordinator;
}

输出始终是“已成功迁移到新数据库位置。”,尽管之前保存在应用程序上的所有数据都已删除,就像它创建了一个新数据库而不是仅仅移动它一样。登记/> 是什么原因造成的?我该如何解决?
谢谢。

2 个答案:

答案 0 :(得分:16)

使用默认选项创建的核心数据NSSQLiteStoreType存储实际上是几个文件,如Technical Q&A 1809: New default journaling mode for Core Data SQLite stores in iOS 7 and OS X Mavericks中所述。在尝试移动迁移过程中的商店时,这一点很重要,并且是您的问题的根源 - 当您需要移动所有文件时,您正在移动一个文件。但是,建议不要在Core Data之外单独移动文件,也不要使用文件协调器的好处。相反,使用迁移要好得多。

迁移将从源存储中获取数据并将其迁移到新的存储位置,基本上复制新位置的旧数据。旧数据仍将存在于文件系统中。在您的应用程序中,您应该像现在一样执行迁移,但不要尝试自己将旧数据移动到新位置 - 这就是出错的地方。

您可以依靠迁移来为您移动数据,而不是自己移动文件。首先,使用源数据的URL将存储添加到持久性存储协调器。然后,您将执行迁移以将该数据移动到新URL

NSPersistentStore   *sourceStore        = nil;
NSPersistentStore   *destinationStore   = nil;
NSDictionary        *storeOptions       = @{ NSSQLitePragmasOption : @{ @"journal_mode" :
  @"WAL" } };

// Add the source store
if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldStoreURL options:storeOptions error:&error]){
    // Handle the error
} else {
    sourceStore = [coordinator persistentStoreForURL:oldStoreURL];
    if (sourceStore != nil){
        // Perform the migration
        destinationStore = [coordinator migratePersistentStore:sourceStore toURL:storeURL options:storeOptions withType:NSSQLiteStoreType error:&error];
        if (destinationStore == nil){
            // Handle the migration error
        } else {
            // You can now remove the old data at oldStoreURL
            // Note that you should do this using the NSFileCoordinator/NSFilePresenter APIs, and you should remove the other files
            // described in QA1809 as well.
        }
    }
}

迁移完成后,您可以删除旧文件。这里的示例显式指定了SQLite日志选项,这是为了确保如果将来更改默认选项,代码仍然可以工作。如果您使用不同的选项,则应使用它们。

答案 1 :(得分:0)

如果在Swift中有一个版本会有所帮助:

    let oldPersistentStoreURL: URL = ...
    let sharedPersistentStoreURL: URL = ...
    let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]    // + any database-specific options

    if FileManager.default.fileExists(atPath: oldPersistentStoreURL.path) {
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
        do {
            try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: oldPersistentStoreURL, options: options)
            if let sourceStore = coordinator.persistentStore(for: oldPersistentStoreURL) {
                let _ = try coordinator.migratePersistentStore(sourceStore, to: sharedPersistentStoreURL, options: options, withType: NSSQLiteStoreType)
                // If migration was successful then delete the old files
            }
        } catch {
            error.logErrors()
        }
    }