2013-09-24 40 views
1

我正在处理UIManagedDocument的轻量级包装,它管理一个人的iCloud帐户中存在多个文档。请参阅:APManagedDocument正确使用iCloud备用存储

我非常依赖在iOS 7中使用备用商店,并尽可能少地关注iCloud的当前状态。因此,让核心数据做基于从WWDC 2013的视频我的理解它最擅长的关于回退店 - 会话207 What’s New in Core Data and iCloud

首先我的经理是如何工作的简要概述: 我创造一切我UIManagedDocuments在当地沙箱并设置适当的持久存储选项以启用iCloud同步。我从不移动沙箱中的UIManagedDocument软件包。

  • 当我想知道云中存在哪些文档时,我执行元数据查询。
  • 当我想打开其中一个文档时,我会先检查它是否存在于本地沙箱中,如果不是在本地沙箱中创建它。 (这需要应用程序需要等待对应于Using local storage: 0消息的通知)。
  • 使用此设置,我永远不需要知道iCloud是启用还是登录。我只是在本地工作,让核心数据执行其与iCloud的事情。

到目前为止,一切是伟大的工作,但我遇到了与使用者之前登录到iCloud中创建一个新文档的情况有点咸菜,我提出下列问题:

  • 我由于没有iCloud进行查询,因此无法执行元数据查询。
  • 由于1,我不得不退回到做一个智能本地扫描,寻找在其路径中具有“local/store/persistentStore”的软件包,并将其列为有效文档。
  • 后来当用户登录时,我的理解是核心数据会将本地存储数据移到云端,但我没有看到。我所看到的是,为iCloud帐户创建了一个新的持久性存储,并且没有数据。

我的一个大问题是当涉及到本地回退存储时,什么是正确的方法?我的假设错在哪里?

这是我需要发布我的iOS 7更新的最终作品之一。任何和所有的反馈将不胜感激,并将反映在我的github项目中,以便其他人可以从我的错误中学习。

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

+0

我已经打开与苹果支持票,希望能够解决这个问题。 – dtrotzjr

+0

@dtrotzr ....支持票的任何更新?我也有同样的问题.... – Max

+0

@Max看到我的答案在下面。苹果终于回到了我的身边,但我对这个答案并不感到兴奋,我觉得这位工程师没有花时间来充分理解我的答案,或者我在回退商店中做了所有的事情。 – dtrotzjr

回答

2

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

基本上我现在所做的是如果用户没有登录我创建文件不启用iCloud同步选项。

然后在启动时如果启用iCloud,我对需要迁移的文档执行扫描,并通过启用iCloud选项打开它们进行迁移。一旦打开,我关闭文档就足以让它们通过元数据扫描进行迁移和扫描。

最后,在完成迁移后,启动新的文档扫描。

它有效,但它有点破解。

参考APManagedDocument承诺: 421aaae

+0

是的,如果iCloud从不可用转换为可用,是否足以使文档重新打开并将其迁移到云中,但在相反的情况下,用户在云中有文档并将iCloud从在设置中打开到关闭?如何将启用了iCloud的文档更改为仅限本地?没有iCloud选项,关闭并重新打开它就足够了吗? –

+0

在这种情况下,您甚至无权访问该文档。你清理你的状态并继续前进。 – dtrotzjr

+0

对不起,我的意思是用户不会在“设置应用程序/文档和数据”中关闭iCloud,而是在“应用程序设置包”中关闭iCloud,就像他可以在iWork中执行操作一样。该设备仍然具有iCloud访问权限,如果用户选择保留设备上的数据,我应该将UIManagedDocument从云端移到本地。 –

2

我终于得到了一个答复今晚。我不是100%肯定他理解我的问题,但在我作出判断之前,我会花一些时间了解他的答案。

这里是他的回应:

谢谢您的查询到的苹果全球开发者技术 支持。我正在回应,通知您我已收到您的 技术援助请求。

Core Data不会自动将您的UIManagedDocument移动到 云中。您需要在无处不在的容器 容器中创建一个新文档,然后将永久存储从本地沙箱迁移到您的无处不在容器。为了创建所有的 事务日志,必须进行迁移,以便其他设备可以创建该文档。

您可以实现这个类的方法你UIManagedDocument 子类:

  • (无效)moveDocumentAtURL:(NSURL *)sourceDocumentURL toUbiquityContainer:(NSURL *)ubiquityContainerURL;

这种方法将基本上在 “ubiquityContainerURL”创建一个新文档,并从 “sourceDocumentURL”到“ubiquityContainerURL”迁移存储。您将使用 “migratePersistentStore”执行迁移。

下面是一个例子:

// 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); 
      } 
     }]; 
    }); 
} 
1

是不是他的反应与你所看到的一致,即没有数据,因为您没有将事务日志放在云中,以便其他设备可以从日志中重新创建文档。通过迁移,我猜你已经在相应的iCloud目录中自动生成了日志文件。但是,207视频似乎表明,不再需要使用任何.sync文件夹。

一个很好的从他们工作的例子就是所有我问...

任何想法,怎么一会从OSX访问这些文件?

顺便说一句,你的东西看起来不错,我希望在几天内尝试使用它。我真的很想看看如何从OSX访问这些文件。据我所知,NSPersistentDocument不是iCloud意识到的。

编辑: 我只是仔细看看你的APManagedDocumentManager,你似乎没有在NSPersistentStoreUbiquitousContentURLKey值中包含iCloud路径。除非我错过了某些东西,而只是使用子目录而不是完整的iCloud路径,否则您使用的是NSString而不是URL(不确定这是否会产生影响)。

编辑:我们应该在电话上讨论一下吧?无论如何,我的一些更多的调查结果如下: 我刚刚安装了小牛,并在两次测试了以下内容之后重新播放了视频: 仅使用以下代码创建新文件 - 没有UIManagedDocument或任何其他内容。 _storeURL指向视频中建议的本地目录。而且我没有使用任何NSPersistentStoreUbiquitousContentURLKey,因为它不再必要。目前我的文件名没有UUID。

当我在任何设备上执行此操作时,会在iCloud Documents目录之外创建一个CoreData目录。 CoreData目录内有每个文件名的子目录,里面有各种zip文件和可能是基线和日志文件的东西。没有任何DocumentMetaData.plist的迹象。所以这一切看起来都非常有希望,除非我无法弄清楚如何“发现”出现的新文件。我希望我只需要注册一些通知,然后完成......现在回到视频中,因为我无法记得有关通知何时发送以及如何响应的详细信息。至少这两种平台的行为是一致的。奇怪的是,没有任何这些文件出现在Mac Apps File-Open对话框中,该对话框列出了iCloud Documents目录中的所有文件,我想这并不奇怪。

enter image description here

_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]; 

...

+0

我正在使用完整的网址,可能会返回。在WWDC 2013视频中,他们只提供表示子文件夹路径组件的NSString值,因此我正在试验该路径以查看迁移是否可行。 – dtrotzjr

+0

我的主要问题在于他提到的.nosync文件夹和视频中他们明确指出,要使用后备商店,我们需要将我们的核心数据文件存储在本地商店中,但他将其放入无处不在的商店中。我看到后备商店功能完全不在应用程序未运行时,它在那个时候我必须介入并执行迁移,在我的实施中仍然有点片面,我不知道它的iCloud缺陷或我的实现。 – dtrotzjr

+0

我可能会尝试将文档存储在无处不在的商店中,并查看后备商店是否仍然有效。我试图尽我所能避免这种情况,但是我一直在触及不存储它们的问题,导致通过元数据查询期间发现的交易收据检测到云中存在哪些文档的问题 – dtrotzjr