Core data 正确使用iCloud后备存储

Core data 正确使用iCloud后备存储,core-data,ios7,icloud,uimanageddocument,Core Data,Ios7,Icloud,Uimanageddocument,我正在为UIManagedDocument开发一个轻量级包装器,用于管理个人iCloud帐户中存在的多个文档。见: 我在很大程度上依赖于iOS 7中后备存储的使用,并且尽可能不关心iCloud的当前状态。因此,根据我对WWDC 2013视频会议207的理解,让核心数据在后备存储方面发挥最佳作用 首先简要介绍一下我的经理的工作方式: 我在本地沙箱中创建所有的UIManagedDocuments,并设置适当的持久存储选项以启用iCloud同步。我从不从沙箱中移动UIManagedDocument包

我正在为UIManagedDocument开发一个轻量级包装器,用于管理个人iCloud帐户中存在的多个文档。见:

我在很大程度上依赖于iOS 7中后备存储的使用,并且尽可能不关心iCloud的当前状态。因此,根据我对WWDC 2013视频会议207的理解,让核心数据在后备存储方面发挥最佳作用

首先简要介绍一下我的经理的工作方式: 我在本地沙箱中创建所有的
UIManagedDocuments
,并设置适当的持久存储选项以启用iCloud同步。我从不从沙箱中移动
UIManagedDocument

  • 当我想知道云中存在哪些文档时,我会执行元数据查询
  • 当我想要打开其中一个文档时,我首先检查它是否存在于本地沙盒中,如果不存在,则在本地沙盒中创建它。 (这要求应用程序需要等待与使用本地存储:0消息的
    对应的通知。)
  • 通过这种设置,我永远不需要知道iCloud是否已启用或登录。我只是在本地工作,让核心数据在iCloud中发挥作用
到目前为止,一切都很顺利,但我遇到了一个小麻烦,用户在登录iCloud之前创建了一个新文档,我遇到了以下问题:

  • 我无法执行元数据查询,因为没有要查询的iCloud
  • 由于1,我不得不返回到智能本地扫描,查找路径中有“local/store/persistentStore”的包,并将其列为有效文档
  • 后来当用户登录时,我了解到核心数据会将我的本地存储数据移动到云上,但我没有看到这一点。相反,我看到的是,为iCloud帐户创建了一个新的持久存储,并且没有数据
我的大问题是,当涉及到本地后备商店时,什么是正确的方法?我的假设哪里错了

这是我需要发布iOS 7更新的最后一部分。任何和所有反馈都将非常感谢,并将反映在我的github项目中,以便其他人可以从我的错误中学习


我在苹果开发者论坛上重复了这个问题。我将用从那里得到的任何发现更新这个线程。我认为这个问题很重要,随着iOS 7的发布,这个问题仍然没有得到解决。后备存储是iCloud技术的一个巨大进步,但本地存储部分仍然有点不明确。

我已经解决了这个问题,因为我似乎无法获得关于后备存储在这种情况下应该如何工作的信息

基本上,我现在要做的是,如果用户未登录,我将创建未启用iCloud同步选项的文档

然后,在启动时,如果启用了iCloud,我会扫描需要迁移的文档,只需在启用iCloud选项的情况下打开它们即可进行迁移。打开后,我关闭文档,因为这足以让它们通过元数据扫描进行迁移和扫描

最后,迁移完成后,我开始对文档进行新的扫描

它可以工作,但有点像黑客

请参阅APManagedDocument提交:

目前我一直在解决这个问题,因为我似乎无法获得关于在这种情况下后备存储应该如何工作的信息

基本上,我现在要做的是,如果用户未登录,我将创建未启用iCloud同步选项的文档

然后,在启动时,如果启用了iCloud,我会扫描需要迁移的文档,只需在启用iCloud选项的情况下打开它们即可进行迁移。打开后,我关闭文档,因为这足以让它们通过元数据扫描进行迁移和扫描

最后,迁移完成后,我开始对文档进行新的扫描

它可以工作,但有点像黑客

请参阅APManagedDocument提交:
今晚我终于得到了答复。我不能百分之百肯定他理解我的问题,但在作出判断之前,我将花一些时间理解他的答案

以下是他的回答:

感谢您向Apple Worldwide Developer Technical咨询 支持我回复你,让你知道我已经收到你的 请求技术援助

核心数据不会自动将UIManagedDocument移动到 云为你。您需要在ubiquity中创建一个新文档 容器然后将持久存储从本地沙箱迁移到 你的无处不在的容器。迁移是创建所有 事务日志,以便其他设备可以创建该文档

您可以在UIManagedDocument中实现该类方法 子类:

  • (void)MoveDocumentUrl:(NSURL*)sourceDocumentURL toUbiquityContainer:(NSURL*)泛素ContaineUrl
该方法实质上是在 “ubiquityContainerURL”,然后将存储从 “sourceDocumentURL”到“UbiquityContaineUrl”。你会用 “migratePersistentStore”来执行迁移

下面是一个例子:


今晚我终于得到了答复。我不能百分之百肯定他理解我的问题,但在作出判断之前,我将花一些时间理解他的答案

以下是他的回答:

感谢您向Apple Worldwide Developer Technical咨询 支持我回复你,让你知道我已经收到你的 请求技术援助

核心数据不会自动将UIManagedDocument移动到 云为你。您需要在ubiquity中创建一个新文档 容器然后将持久存储从本地沙箱迁移到 你的无处不在的容器。迁移对于创建一个
// The name of the file that contains the store identifier.
static NSString *DocumentMetadataFileName = @"DocumentMetadata.plist";

// The name of the file package subdirectory that contains the Core Data store when local.
static NSString *StoreDirectoryComponentLocal = @"StoreContent";

// The name of the file package subdirectory that contains the Core Data store when in the cloud. The Core Data store itself should not be synced directly, so it is placed in a .nosync directory.
static NSString *StoreDirectoryComponentCloud = @"StoreContent.nosync";

+ (NSDictionary *)optionsForStoreAtURL:(NSURL *)url {

    NSURL *metadataDictionaryURL = [url URLByAppendingPathComponent:DocumentMetadataFileName];
    NSDictionary __block *storeMetadata = nil;

    /*
     Perform a coordinated read of the store metadata file; the coordinated read ensures it is downloaded in the event that the document is cloud-based.
     */
    NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
    [fileCoordinator coordinateReadingItemAtURL:metadataDictionaryURL options:0 error:NULL byAccessor:^(NSURL *newURL) {
        storeMetadata = [[NSDictionary alloc] initWithContentsOfURL:newURL];
    }];

    NSString *persistentStoreUbiquitousContentName = nil;

    if (storeMetadata != nil) {

        persistentStoreUbiquitousContentName = [storeMetadata objectForKey:PersistentStoreUbiquitousContentNameKey];
        if (persistentStoreUbiquitousContentName == nil) {
            // Should not get here.
            NSLog(@"ERROR in optionsForStoreAtURL:");
            NSLog(@"persistentStoreUbiquitousContentName == nil");
            abort();
        }
    }
    else {

        CFUUIDRef uuid = CFUUIDCreate(NULL);
        CFStringRef uuidString = CFUUIDCreateString(NULL, uuid);
        persistentStoreUbiquitousContentName = (__bridge_transfer NSString *)uuidString;
        CFRelease(uuid);
    }

    // NSPersistentStoreUbiquitousContentURLKey should be the TransactionLogs directory.

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             persistentStoreUbiquitousContentName, NSPersistentStoreUbiquitousContentNameKey,
                             [[self URLForUbiquityTransactionLogs] URLByAppendingPathComponent:persistentStoreUbiquitousContentName] , NSPersistentStoreUbiquitousContentURLKey, nil];

    return options;
}

+ (void)moveDocumentAtURL:(NSURL *)sourceDocumentURL toUbiquityContainer:(NSURL *)ubiquityContainerURL {

    if (ubiquityContainerURL == nil) {

        // iCloud isn't configured.
        NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                              NSLocalizedString(@"iCloud does not appear to be configured.", @""), NSLocalizedFailureReasonErrorKey, nil];
        NSError *error = [NSError errorWithDomain:@"Application" code:404 userInfo:dict];
        NSLog(@"%@", [error localizedFailureReason]);
        return;
    }

    // Move the document to the cloud using its existing filename
    NSManagedObjectModel *model = [self managedObjectModel];
    NSDictionary *ubiquitousOptions = [self optionsForStoreAtURL:sourceDocumentURL];

    NSString *documentName = [[sourceDocumentURL lastPathComponent] stringByDeletingPathExtension];
    documentName = [documentName stringByAppendingPathExtension:@"wwWhat"];
    NSURL *destinationURL = [ubiquityContainerURL URLByAppendingPathComponent:documentName];

    dispatch_queue_t q_default;
    q_default = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(q_default, ^{

        NSError __block *error = nil;

        NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
        [coordinator coordinateWritingItemAtURL:destinationURL options:NSFileCoordinatorWritingForReplacing error:nil byAccessor:^(NSURL *destination) {

            NSFileManager *fileManager = [[NSFileManager alloc] init];
            [fileManager removeItemAtURL:destination error:nil];

            NSURL *destinationStoreDirectoryURL = [destination URLByAppendingPathComponent:StoreDirectoryComponentCloud isDirectory:YES];
            NSURL *destinationStoreURL = [destinationStoreDirectoryURL URLByAppendingPathComponent:StoreFileName isDirectory:NO];

            NSURL *sourceStoreURL = [[sourceDocumentURL URLByAppendingPathComponent:StoreDirectoryComponentLocal isDirectory:YES] URLByAppendingPathComponent:StoreFileName isDirectory:NO];
            NSURL *originalMetadataURL = [sourceDocumentURL URLByAppendingPathComponent:DocumentMetadataFileName isDirectory:NO];
            NSURL *destinationMetadataURL = [destination URLByAppendingPathComponent:DocumentMetadataFileName isDirectory:NO];

            [fileManager createDirectoryAtURL:destinationStoreDirectoryURL withIntermediateDirectories:YES attributes:nil error:nil];
            [fileManager copyItemAtURL:originalMetadataURL toURL:destinationMetadataURL error:nil];

            NSPersistentStoreCoordinator *pscForSave = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: model];
            id store = [pscForSave addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sourceStoreURL options:nil error:nil];

            id success = [pscForSave migratePersistentStore:store toURL:destinationStoreURL options:ubiquitousOptions withType:NSSQLiteStoreType error:&error];

            if (success) {
                [fileManager removeItemAtURL:sourceDocumentURL error:NULL];
            }
            else {
                NSLog(@"Failed to migrate store: %@", error);
            }
        }];
    });
}
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
//FLOG(@"  got_persistentStoreCoordinator is %@", _persistentStoreCoordinator);
FLOG(@"  calling addPersistentStoreWithType for path %@", [_storeURL path]);
NSString *fileName = [[_storeURL URLByDeletingPathExtension] lastPathComponent];
FLOG(@"  setting NSPersistent for path %@", [_storeURL path]);
@try {
    store = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:_storeURL
                                                            options:@{NSPersistentStoreUbiquitousContentNameKey:fileName,
                                                              NSMigratePersistentStoresAutomaticallyOption:@YES,
                                                              NSInferMappingModelAutomaticallyOption:@YES,
                                                              NSSQLitePragmasOption:@{ @"journal_mode" : @"DELETE" }}
                                                              error:&error];