【问题标题】:iCloud Core Data sharing between OSX and iOSOSX 和 iOS 之间的 iCloud 核心数据共享
【发布时间】:2015-11-26 15:30:09
【问题描述】:

我有一个适用于 OSX 和 iOS 的应用程序,并且都使用 iCloud 在它们之间共享核心数据。当我在 iPhone 中进行更改时,我可以在 OSX 应用程序中看到它们,并且在两个应用程序中都调用了 NSPersistentStoreDidImportUbiquitousContentChangesNotification,因此 iOS -> Mac 运行良好。但是当我在 Mac 应用程序上进行一些更改时,我无法在 iPhone 中看到它们。 iPhone 从不调用NSPersistentStoreDidImportUbiquitousContentChangesNotification。我可以获得 Mac 更改的唯一方法是在 iPhone 上进行一些更改。然后我可以看到所有的变化。

两个项目中的 iCloud 集成是相同的。核心数据数据模型是相同的。权利也是一样的。

我所有的代码都基于这个库:http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/

为什么在我对 iPhone 进行一些更改之前,iPhone 没有获得 Mac 更改?有什么想法吗?

另一个问题

在我的应用程序中,当用户启用 iCloud 时,我会检查是否已经是 iCloud 中的文件。如果文件存在,用户可以选择从 iCloud 文件恢复核心数据或从本地存储开始新副本。 要开始一个新副本,我使用以下代码:

- (void)startNewCopy
{
    [self removeICloudStore];
    [self moveStoreToIcloud];
}

- (void)removeICloudStore
{
    BOOL result;
    NSError *error;
    // Now delete the iCloud content and file
    result = [NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:[self icloudStoreURL]
                                                                             options:[self icloudStoreOptions]
                                                                             error:&error];
    if (!result)
    {
        NSLog(@"Error removing store");
    }
    else
    {
        NSLog(@"Core Data store removed.");
        // Now delete the local file
        [self deleteLocalCopyOfiCloudStore];
    }

}


- (void)deleteLocalCopyOfiCloudStore {
   // We need to get the URL to the store
   NSError *error = nil;
   [[NSFileManager defaultManager] removeItemAtURL:[self localUbiquitySupportURL] error:&error];
}

- (void)moveStoreToIcloud
{
     // Open the store
     NSPersistentStore *sourceStore = [[_persistentStoreCoordinator persistentStores] firstObject];

     if (!sourceStore)
     {
         NSLog(@" failed to add old store");
     }
     else
     {
         NSLog(@" Successfully added store to migrate");
         NSError *error;
         id migrationSuccess = [_persistentStoreCoordinator migratePersistentStore:sourceStore toURL:[self icloudStoreURL] options:[self icloudStoreOptions] withType:NSSQLiteStoreType error:&error];

         if (migrationSuccess)
         {
             NSLog(@"Store migrated to iCloud");

             _persistentStoreCoordinator = nil;
             _managedObjectContext = nil;

             // Now delete the local file
             [self deleteLocalStore];

         }
         else
         {
             NSLog(@"Failed to migrate store: %@, %@", error, error.userInfo);

         }
     }

}

- (void)deleteLocalStore
{
     NSError *error = nil;
     [[NSFileManager defaultManager] removeItemAtURL:[self localStoreURL] error:&error];
}

在单个设备上执行此操作似乎一切正常,但是当我在多个设备上尝试此操作时,我遇到了一些错误。

例子:

我有两个设备。第一个未连接到 iCloud,第二个连接到 iCloud。 在我启用 iCloud 的第一台设备中,我选择 startNewCopy,然后在第二台设备中出现此错误:

CoreData: Ubiquity: Librarian 在开始下载时返回了一个严重错误 Error Domain=BRCloudDocsErrorDomain Code=5 "The operation could not be completed. (BRCloudDocsErrorDomain error 5 - No document at URL)"

NSPersistentStoreCoordinatorStoresDidChangeNotification 在错误之前从未被调用。

这是我的通知代码:

- (void)processStoresDidChange:(NSNotification *)notification
{
      NSLog(@"processStoresDidChange");
      // Post notification to trigger UI updates

      // Check type of transition
      NSNumber *type = [notification.userInfo objectForKey:NSPersistentStoreUbiquitousTransitionTypeKey];

      //NSLog(@" userInfo is %@", notification.userInfo);
      //NSLog(@" transition type is %@", type);

      if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted) {

          NSLog(@" transition type is NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted");

      } else if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeAccountAdded) {
          NSLog(@" transition type is NSPersistentStoreUbiquitousTransitionTypeAccountAdded");
      } else if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeAccountRemoved) {
          NSLog(@" transition type is NSPersistentStoreUbiquitousTransitionTypeAccountRemoved");
      } else if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeContentRemoved) {
          NSLog(@" transition type is NSPersistentStoreUbiquitousTransitionTypeContentRemoved");
      }

      [[NSOperationQueue mainQueue] addOperationWithBlock:^ {

          if (type.intValue == NSPersistentStoreUbiquitousTransitionTypeContentRemoved) {

              [self deleteLocalStore];
              _persistentStoreCoordinator = nil;
              _managedObjectContext = nil;

              NSLog(@" iCloud store was removed! Wait for empty store");
          }

          // Refresh user Interface
          [[NSNotificationCenter defaultCenter] postNotificationName:@"notiIcloud" object:@"storeChanged"];
      }];

}

如何检测到 iCloud 内容已被删除并避免出现上述错误?

【问题讨论】:

  • 这并不能真正回答你的问题,但我很久以前就放弃了使用支持 iCloud 的 Core Data。然后我编写了自己的同步,但它有点受限。然后我找到了 Ensembles (ensembles.io) 并且一直在使用它。这对我来说太棒了。有一个免费的开源版本。
  • 谢谢回答,但我更喜欢使用苹果的标准 iCloud 支持。
  • 我写了你正在使用的代码,你测试的是什么版本的 iOS、OSX 和 XCode?​​span>
  • ios 8,osx 10.10,xcode 6.3。 iphone之间的同步效果很好。我需要确切地知道在 mac 和 iphone 之间同步核心数据的要求是什么。也许我忘记了什么:S
  • 您是否尝试过编译和运行示例应用程序,除了容器 ID 之外没有任何更改?它们应该开箱即用,尽管请记住该代码中未涵盖一些边缘情况。还要检查 Mac 上的 iCloud 文件夹以查看是否所有事务文件都在那里,使用终端会话并输入类似 ls -R "Library/Mobile Documents/iCloud~au~com~ossh~iWallet2" 的内容

标签: ios macos core-data icloud


【解决方案1】:

在我看来,您可能没有使用适当的接口。

您可以在不使用NSPersistentStore 方法的情况下将文件移入和移出iCloud。 通常,适当的操作也必须包含在 NSFileCoordinator 调用中。

这是我在 iOS 上所做的。 toLocal 表示移入或移出本地(设备)存储:

//
/// Move file between stores (assumed no name clash).
/// Return new URL.
/// Note: cloud file moves are asynchronous.
///
- (NSURL *)moveStoreFile:(NSURL *)url toRootURL:(NSURL *)rootURL toLocal:(BOOL)toLocal
{
    NSURL *newURL = rootURL;
    if (!toLocal)
        newURL = [newURL URLByAppendingPathComponent:@"Documents"];
    newURL = [newURL URLByAppendingPathComponent:url.lastPathComponent];

    [self backupFile:url isLocal:!toLocal completionHandler:
     ^(BOOL success) {

        MRLOG(@"MOVE %s %@ to %s %@", toLocal? "icloud" : "local", [url lastPathComponent],
              toLocal? "local" : "icloud", [newURL lastPathComponent]);

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
        ^{
           NSError *error;
           // setUbiquitous does file coordination
           BOOL msuccess = [[NSFileManager defaultManager]
                           setUbiquitous:!toLocal itemAtURL:url destinationURL:newURL error:&error];

           dispatch_async(dispatch_get_main_queue(),
           ^{
              if (!msuccess)
                  MRLOG(@"move failed: %@", error);
              [self.delegate moveStoreFileCompletedWithStatus:success error:error];
           });
        });
    }];

    return newURL;
}

删除需要明确的文件协调:

///
/// Purge backup file (really delete it).
/// Note: cloud deletes are asynchronous.
///
- (void)purgeFile:(NSURL *)url isLocal:(BOOL)isLocal
{
    MRLOG(@"PURGE %s %@", isLocal? "local" : "icloud", [url lastPathComponent]);

    if (isLocal)
        [self removeFile:url];
    else
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
        ^{
            NSError *coordinationError;
            NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];

            [coordinator coordinateWritingItemAtURL:url options:NSFileCoordinatorWritingForDeleting
                                                    error:&coordinationError
                                     byAccessor:
             ^(NSURL* writingURL)
             {
                 [self removeFile:writingURL];
             }];
            if (coordinationError) {
                MRLOG(@"coordination error: %@", coordinationError);
                [(SSApplication *)[SSApplication sharedApplication] fileErrorAlert:coordinationError];
            }
        });
}

要检测对文件的更改,您需要使用NSMetadataQuery 并为NSMetadataQueryDidUpdateNotification 添加观察者。

【讨论】:

    【解决方案2】:

    我在将 iOS 与我的 Mac 同步时遇到了类似的问题。

    此错误 5 实际上意味着您的商店存在问题。

    您可以尝试以下步骤:

    • 在 iOS 上,您需要重置模拟器的内容并清除 iCloud 内容(仅限您的应用)。

    您可以通过调用重置您的 iCloud 内容(对不起,我在 Swift 中有):

    try! NSPersistentStoreCoordinator.removeUbiquitousContentAndPersistentStoreAtURL(storeURL, options: self.options)
    
    • 在 Mac OS 上,您需要删除此文件夹“CoreDataUbiquitySupport/YourAppId”的内容。这基本上是您的核心数据持久存储(例如:SQLite 文件)。

    当模型(xcdatamodeld 文件)更改且现有数据尚未清除时,会发生此问题。您最终拥有一个与旧模型下生成的数据相关联的新模型,并且您正在尝试跨设备同步该混合...

    将来,为了避免与模型相关的问题,请查看 Core Data Model Versioning 和 Data Migration。 https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html

    希望它能解决你的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-01
      • 1970-01-01
      • 2017-03-23
      • 1970-01-01
      • 2012-09-27
      • 2020-03-13
      • 1970-01-01
      • 2014-05-30
      相关资源
      最近更新 更多