iCloud核心数据在OSX和iOS之间的共享

12
我有一个在OSX和iOS上使用iCloud共享核心数据的应用程序。当我在iPhone上进行更改时,我可以在OSX应用程序中看到它们,并且在两个应用程序中都调用了NSPersistentStoreDidImportUbiquitousContentChangesNotification,因此iOS -> Mac正常工作。但是,当我在Mac应用程序中进行某些更改时,我无法在iPhone中看到它们。iPhone从未调用NSPersistentStoreDidImportUbiquitousContentChangesNotification。唯一的方法是在iPhone上进行某些更改,然后我就可以看到所有更改。
两个项目中的iCloud集成相同。核心数据数据模型也相同。授权也都相同。
我的所有代码都基于这个库:http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/ 为什么iPhone没有收到Mac更改,直到我在iPhone上进行一些更改?有任何想法吗?
另一个问题:
在我的应用程序中,当用户启用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:图书管理员返回了一个严重的错误以开始下载 错误域=BRCloudDocsErrorDomain Code=5 "The operation couldn’t 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(http://www.ensembles.io),从那时起我一直在使用它。对我来说,它非常棒。还有一个免费的开源版本。 - Jody Hagins
谢谢回答,但我更喜欢使用苹果的标准iCloud支持。 - user3065901
1
我写了你正在使用的那段代码,你在测试时用的是哪个版本的iOS、OSX和XCode? - Duncan Groenewald
iOS 8,OSX 10.10,Xcode 6.3。iPhone之间的同步工作正常。我需要确切地知道在Mac和iPhone之间同步Core Data所需的要求。也许我忘记了什么:S - user3065901
你尝试过使用容器ID编译和运行示例应用程序吗?它们应该可以直接使用,但请注意代码中可能存在一些未覆盖的边缘情况。此外,请检查您Mac上的iCloud文件夹,看看所有交易文件是否都在那里,使用终端会话并键入类似于ls -R "Library/Mobile Documents/iCloud~au~com~ossh~iWallet2"的命令。 - Duncan Groenewald
当然,您需要设置App ID和配置文件,但是如果iPhone应用程序正在工作,我认为您已经完成了此操作。最好的方法是编译和测试我提供的示例应用程序,如果它们无法正常工作,我会进行检查。 - Duncan Groenewald
2个回答

0

我觉得您可能没有使用适当的接口。

您可以在不使用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添加观察者。

0

我曾经遇到过将iOS与我的Mac同步的类似问题。

实际上,错误代码5表示您的商店存在问题。

您可以尝试以下步骤:

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

您可以通过调用以下代码来重置iCloud上的内容(抱歉,这是Swift代码):

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

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

为避免模型相关问题,请查看核心数据模型版本控制和数据迁移。https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html

希望它能解决您的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接