【问题标题】:Mac / iPhone app - syncing core data to iCloud & devices (using core data)Mac / iPhone 应用程序 - 将核心数据同步到 iCloud 和设备(使用核心数据)
【发布时间】:2011-12-30 13:30:27
【问题描述】:

我正在开发一个使用 Core Data 的 iPhone 应用和一个 Mac 应用。

我想让这 2 个应用通过 iCloud 存储同步它们的数据库。

我已对 managedObjectContextpersistentStoreCoordinator 的实现进行了调整并添加了 mergeiCloudChanges - 来自更新的食谱示例代码:

#pragma mark -
#pragma mark Core Data stack

// this takes the NSPersistentStoreDidImportUbiquitousContentChangesNotification
// and transforms the userInfo dictionary into something that
// -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] can consume
// then it posts a custom notification to let detail views know they might want to refresh.
// The main list view doesn't need that custom notification because the NSFetchedResultsController is
// already listening directly to the NSManagedObjectContext
- (void)mergeiCloudChanges:(NSNotification*)note forContext:(NSManagedObjectContext*)moc {

    NSLog(@"merging iCloud stuff");

    [moc mergeChangesFromContextDidSaveNotification:note]; 

    NSNotification* refreshNotification = [NSNotification notificationWithName:@"RefreshAllViews" object:self  userInfo:[note userInfo]];

    [[NSNotificationCenter defaultCenter] postNotification:refreshNotification];
}

/**
 Returns the managed object context for the application.
 If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
 */
- (NSManagedObjectContext *)managedObjectContext
{
    if (managedObjectContext != nil)
    {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

    if (coordinator != nil)
    {
        if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"5.0")) {
            NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

            [moc performBlockAndWait:^{
                [moc setPersistentStoreCoordinator: coordinator];

                [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:coordinator];
            }];
            managedObjectContext = moc;
        } else {
            managedObjectContext = [[NSManagedObjectContext alloc] init];
            [managedObjectContext setPersistentStoreCoordinator:coordinator];
        }

    }
    return managedObjectContext;
}

// NSNotifications are posted synchronously on the caller's thread
// make sure to vector this back to the thread we want, in this case
// the main thread for our views & controller
- (void)mergeChangesFrom_iCloud:(NSNotification *)notification {


     NSManagedObjectContext* moc = [self managedObjectContext];

    // this only works if you used NSMainQueueConcurrencyType
    // otherwise use a dispatch_async back to the main thread yourself
    [moc performBlock:^{
        [self mergeiCloudChanges:notification forContext:moc];
    }];
}


/**
 Returns the managed object model for the application.
 If the model doesn't already exist, it is created by merging all of the models found in the application bundle.
 */
- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel != nil) {
        return managedObjectModel;
    }
    managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];    
    return managedObjectModel;
}





- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator__ != nil) {
        return persistentStoreCoordinator__;
    }

    // assign the PSC to our app delegate ivar before adding the persistent store in the background
    // this leverages a behavior in Core Data where you can create NSManagedObjectContext and fetch requests
    // even if the PSC has no stores.  Fetch requests return empty arrays until the persistent store is added
    // so it's possible to bring up the UI and then fill in the results later
    persistentStoreCoordinator__ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];


    // prep the store path and bundle stuff here since NSBundle isn't totally thread safe
    NSPersistentStoreCoordinator* psc = persistentStoreCoordinator__;
     NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"MyApp.sqlite"];

    // do this asynchronously since if this is the first time this particular device is syncing with preexisting
    // iCloud content it may take a long long time to download
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSFileManager *fileManager = [NSFileManager defaultManager];

        NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
        // this needs to match the entitlements and provisioning profile
        NSURL *cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil];
        NSString* coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"MyApp"];
        cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];

        NSLog(@"cloudURL: %@", cloudURL);        

        //  The API to turn on Core Data iCloud support here.
        NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@"xxxxxxxx.com.me.MyApp", 
                                 @"MyApp", 
                                 cloudURL, 
                                 NSPersistentStoreUbiquitousContentURLKey, 
                                 [NSNumber numberWithBool:YES], 
                                 NSMigratePersistentStoresAutomaticallyOption, 
                                 [NSNumber numberWithBool:YES], 
                                 NSInferMappingModelAutomaticallyOption,
                                 nil];

        NSError *error = nil;

        [psc lock];
        if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
            /*
             Replace this implementation with code to handle the error appropriately.

             abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.

             Typical reasons for an error here include:
             * The persistent store is not accessible
             * The schema for the persistent store is incompatible with current managed object model
             Check the error message to determine what the actual problem was.
             */
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }    
        [psc unlock];

        // tell the UI on the main thread we finally added the store and then
        // post a custom notification to make your views do whatever they need to such as tell their
        // NSFetchedResultsController to -performFetch again now there is a real store
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"asynchronously added persistent store!");
            [[NSNotificationCenter defaultCenter] postNotificationName:@"RefetchAllDatabaseData" object:self userInfo:nil];
        });
    });

    return persistentStoreCoordinator__;
}

当我构建/运行 myapp 时,我可以看到文件出现在我的“/Users/me/Library/Mobile Documents”目录中。
但我不知道它是否正在同步到 iCloud 存储 - 显然 iphone 和 mac 之间的数据没有同步。
是否需要实施其他方法才能将数据移至云端?
有什么方法可以让我查看 iCloud 存储中实际有哪些文件?

【问题讨论】:

    标签: ios macos core-data icloud nspersistentdocument


    【解决方案1】:

    另一个部分答案。如果您还没有,请查看:http://www.raywenderlich.com/6015/beginning-icloud-in-ios-5-tutorial-part-1。我正在和你做同样的事情,即 Mac App 和 iOS 应用程序共享数据。祝你好运。标记

    我刚刚了解到:http://mentalfaculty.tumblr.com/archive 寻找“Under The Sheets”CoreData 和 iCloud。看看吧!

    【讨论】:

      【解决方案2】:

      好的,我的代码看起来有点不同,我将它放在一个单独的类中,以便在我的所有项目中重用它。不过,如果启用了 iCloud(URLForUbiquityContainerIdentifier:nil return not nil),我会像这样设置我的 NSPersistentStoreCoordinator:

      // ---- iCloud Setup 
      
      // fist container in entitlements
      NSURL *iCloudDirectoryURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; 
      
      // if iCloud is enabled setup the store
      if (iCloudDirectoryURL) {
      
          __iCloudEnabled = true;
      
          NSLog(@"iCloud:%@", [iCloudDirectoryURL absoluteString]);
          // AppDelegate has to provide the contentnamekey
          NSString *contentNameKey = [appDelegate dataStoreContentNameKey];
      
           options = [NSDictionary dictionaryWithObjectsAndKeys:contentNameKey, NSPersistentStoreUbiquitousContentNameKey, iCloudDirectoryURL, NSPersistentStoreUbiquitousContentURLKey, nil];
      }
      

      我想念你设置 NSPersistentStoreUbiquitousContentNameKey 的位置。

      我猜第二个很清楚,这仅适用于设备并且您的 App ID 需要启用 iCloud。

      【讨论】:

        【解决方案3】:

        这是一个快速的部分答案。

        您可以查看 iCloud 中存储的内容:

        在 Mac 上:

        System Preferences.app -> iCloud -> 点击“管理...”,然后您将看到所有存储了 Mac OS X 或 iOS 文档的应用程序列表。

        在 iOS 上:

        Preferences -> iCloud -> Archive & Backup -> Space used 下面的选项然后,您将看到存储了 Mac OS X 或 iOS 文档的所有应用程序的列表。

        只要您使用NSFileManagersetUbiquitous: itemAtURL: destinationURL: error:,文档就会为您发送到 iCloud 并显示在其他设备上。

        【讨论】:

        • 嗨,迈克,感谢您的回复。我已经通过 sys prefs 检查了我的 iCloud 存储。看起来没有任何东西可以复制。好继续找。还有其他提示吗?也是的,我正在使用 NSFileManager 的 setUbiquitous
        • 啊实际上不,我没有使用“setUbiquitous:itemAtURL:destinationURL:error:” - 我正在使用“addPersistentStoreWithType”并将包含 NSPersistentStoreUbiquitousContentNameKey 的选项字典传递给它这是同样的想法吗?我会读...再次感谢
        • 我注意到我的应用程序挂在这一行:if ([[NSFileManager defaultManager] setUbiquitous:makeUbiquitous itemAtURL:fileURL destinationURL:destURL error:&err]) { 所以我从 Xcode 中停止了应用程序,然后它出现在控制台应用程序中:librariand: client connection is invalid: Connection invalid 有什么想法吗?
        • 连接无效消息只是意味着应用程序消失了,因此没有更多的连接。如果您使用的是基于文档的应用程序,则需要使用 setUbiquitous:...。当我遇到问题时,我将 NSLog(@"container == %@", [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:container]); 添加到应用程序中的一个 init 方法中,以查看我是否获得了 URL。您还应该确保在将文档首先写入磁盘之前不要尝试移动它。
        • 嗨,马克,感谢您的帮助,我已经改变了我的应用程序现在的工作方式,我终于可以看到我的 iPhone 和 Mac 上发生了某种同步。虽然我不能说这完全顺利。我要问一个关于我的新问题的新问题!再次感谢您的帮助!
        猜你喜欢
        • 2016-07-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-25
        • 2014-01-29
        • 2012-01-21
        • 1970-01-01
        相关资源
        最近更新 更多