2011-11-16 67 views
6

我正在使用iPhone應用程序和使用核心數據的Mac應用程序。Mac/iPhone應用程序 - 將核心數據同步到iCloud和設備(使用核心數據)

我想讓這兩個應用程序通過iCloud存儲同步它們的數據庫。

我已經做了調整的實現的managedObjectContext & persistentStoreCoordinator &添加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。
但我不知道它是否正在同步到iCloud存儲 - 顯然iphone和mac之間的數據沒有同步。
我需要執行哪些其他方法才能將數據移至雲端?
我有什麼方法可以查看iCloud存儲上實際存在的文檔嗎?

回答

3

這裏是一個快速的部分答案。

你可以看到什麼是存儲在iCloud中:

在Mac上:

系統Preferences.app - > iCloud中 - >點擊 '管理...' 然後你會看到所有的列表具有存儲Mac OS X或iOS文檔的應用程序。

在iOS上:

首選項 - > iCloud中 - >存檔&備份 - >選項下方的空間使用然後你會看到有存儲的MAC OS X或iOS文件的所有應用程序的列表。

只要您使用的是NSFileManagersetUbiquitous: itemAtURL: destinationURL: error:這些文檔應該會發送給iCloud,並顯示在其他設備上。

+0

嗨邁克,謝謝你的回覆。 我已通過sys首選項查看了我的iCloud存儲。它看起來沒有任何複製。那麼繼續尋找。 任何其他提示? 還不錯,我使用的NSFileManager的setUbiquitous – adamteale

+0

啊其實我沒有使用任何「setUbiquitous:itemAtURL:DESTINATIONURL:錯誤:」 - 我使用「addPersistentStoreWithType」和它傳遞的選項字典其中包含NSPersistentStoreUbiquitousContentNameKey 這是同樣的想法? 我已閱讀... 再次感謝 – adamteale

+0

我注意到,我的應用程序掛起,在這一行: '如果([的NSFileManager defaultManager] setUbiquitous:makeUbiquitous itemAtURL:fileURL DESTINATIONURL:destURL錯誤:ERR]){' 所以我停止從Xcode應用程序,然後這出現在控制檯應用程序: 'librariand:客戶端連接無效:連接無效' 任何想法? – adamteale

0

確定我的代碼看起來有點不同,我將它放在一個單獨的類中,以便在我的所有項目中重複使用它。然而,如果iCloud的啓用(URLForUbiquityContainerIdentifier:無返回值不爲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。

第二個是我很清楚,這隻適用於設備和您的應用程序ID需要啓用iCloud。