2013-10-17 166 views
1

我正在開發iOS 7應用程序,我對iCloud和Core數據相當陌生。 我的假設是用戶總是希望儘可能使用iCloud for Backup。這就是爲什麼我建立了我的持久性存儲如下:iCloud核心數據遷移

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { 

if (__persistentStoreCoordinator != nil) { 

     return __persistentStoreCoordinator; 

} 

NSURL *storeURL = [[[self applicationDocumentsDirectory] 

           URLByAppendingPathComponent:storeNameWithoutFileExtension isDirectory:NO] 

           URLByAppendingPathExtension:@"sqlite"]; 

NSError *error = nil; 

__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 



[[NSNotificationCenter defaultCenter] addObserver:self 

                selector:@selector(persistentStoreDidImportUbiquitiousContentChanges:) 

                 name:NSPersistentStoreDidImportUbiquitousContentChangesNotification 

                 object:__persistentStoreCoordinator]; 



[[NSNotificationCenter defaultCenter] addObserver:self 

                selector:@selector(storesWillChange:) 

                 name:NSPersistentStoreCoordinatorStoresWillChangeNotification 

                 object:__persistentStoreCoordinator]; 



[[NSNotificationCenter defaultCenter] addObserver:self 

                selector:@selector(storesDidChange:) 

                 name:NSPersistentStoreCoordinatorStoresDidChangeNotification 

                 object:__persistentStoreCoordinator]; 





NSDictionary *options = @{ NSInferMappingModelAutomaticallyOption : @YES, 

            NSPersistentStoreUbiquitousContentNameKey : storeNameWithoutFileExtension, }; 

// NSPersistentStoreRebuildFromUbiquitousContentOption 

// NSMigratePersistentStoresAutomaticallyOption: @YES, 

// NSMigratePersistentStoresAutomaticallyOption : @YES, 



if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 

                  configuration:nil 

                     URL:storeURL 

                    options:options 

                     error:&error]) 

{ 

     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 

     abort(); 

} 



return __persistentStoreCoordinator; 

} 

在此設置下,我總是立即開始使用下文檔的文件夾/ CoreDataUbiquitySupport即使用戶沒有登錄到iCloud(使用「本地」文件夾) 。

正如您所看到的,我正在註冊通知。

  • 爲什麼在鍵NSAddedPersistentStoresKey和NSRemovedPersistentStoresKey在NSPersistentStoreCoordinatorStoresWillChangeNotification和NSPersistentStoreCoordinatorStoresDidChangeNotification總是相同的例如的NSNotifications的陣列中的添加和刪除的持久性存儲爲案例帳戶添加? 人們會期望通知包含已刪除的持久性存儲和新添加的。所以他們不應該是一樣的。但不幸的是他們總是一樣的。

當前的行爲是,如果我切換帳戶或註銷帳戶或登錄到新帳戶,則應用程序始終從關聯的商店開始,如果以前沒有開始,則新的應用程序開始。我希望的行爲是應用程序總是將數據遷移到新的商店。例如:用戶未登錄到iCloud並開始使用我的應用程序。他在Documents/CoreDataUbiquitySupport中創建保存在本地下的數據。現在他打開了iCloud。現在我想將本地數據遷移到雲端。目前這沒有發生。 我需要在哪種方法中實現遷移,以及我如何實現它?我可以使用方法「migratePersistentStore」嗎?我試着用下面的代碼在NSPersistentStoreCoordinatorStoresDidChangeNotification但無濟於事:

- (void)storesDidChange:(NSNotification *)n { 

// refresh user interface 
switch ([[n.userInfo objectForKey:NSPersistentStoreUbiquitousTransitionTypeKey] integerValue]) { 

     case NSPersistentStoreUbiquitousTransitionTypeAccountAdded : { 

      NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountAdded migrate"); 


      NSError *error = nil; 

      NSURL *storeURL = ((NSPersistentStore *)asdf[0]).URL; 

      NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption : @YES, 

              NSInferMappingModelAutomaticallyOption : @YES, 

              NSPersistentStoreUbiquitousContentNameKey : storeNameWithoutFileExtension, }; 

      NSPersistentStore *asdfasdf = [__persistentStoreCoordinator migratePersistentStore:self.oldStore toURL:storeURL options:options withType:NSSQLiteStoreType error:&error]; 
     } 

     break; 

     case NSPersistentStoreUbiquitousTransitionTypeAccountRemoved : 

      NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountRemoved"); 



      break; 

     case NSPersistentStoreUbiquitousTransitionTypeContentRemoved : { 

      NSLog(@"NSPersistentStoreUbiquitousTransitionTypeContentRemoved migrate"); 

      NSError *error = nil; 

      NSURL *storeURL = ((NSPersistentStore *)asdf[0]).URL; 

      NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption : @YES, 

              NSInferMappingModelAutomaticallyOption : @YES, 

              NSPersistentStoreUbiquitousContentNameKey : storeNameWithoutFileExtension, }; 

      NSPersistentStore *asdfasdf = [__persistentStoreCoordinator migratePersistentStore:self.oldStore toURL:storeURL options:options withType:NSSQLiteStoreType error:&error]; 

     } 

     break; 

     case NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted : 

      NSLog(@"NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted"); 

      NSLog(@"initial import"); 



      break; 

     default : 

      break; 

} 

} 

變量oldStore是在NSPersistentStoreCoordinatorStoresWillChangeNotification設置,因爲我不能依靠NSNotification及其鍵NSAddedPersistentStoresKey和NSRemovedPersistentStoresKey因爲此前兩國都陣列始終包含與前述相同的商店。

  • 在哪種方法中/在什麼時候我會執行重複數據刪除代碼?我可以在其中一個通知中完成嗎?

  • 對於哪個用例,我必須使用NSMigratePersistentStoresAutomaticallyOption?這個選項如何工作?

我在使用我的應用程序時遇到的奇怪行爲是,如果用戶未登錄到iCloud。當我將應用程序置於後臺並退出iCloud並返回到我的應用程序時,我得到的NSNotifications類型爲NSPersistentStoreUbiquitousTransitionTypeContentRemoved。我期望NSPersistentStoreUbiquitousTransitionTypeAccountRemoved。如果我然後將該應用程序置於後臺並再次備份,則會獲取類型爲NSPersistentStoreUbiquitousTransitionTypeAccountRemoved的NSNotifications。但不幸的是,當用戶未登錄到iCloud並且應用程序正在運行並且用戶將該應用程序置於後臺並返回前臺時,NSPersistentCoordinatorStoresWillChangeNotification和NSPersistentStoreCoordinatorStoresDidChangeNotification將始終爲用例觸發,並且我得到類型爲NSPersistentStoreUbiquitousTransitionTypeAccountRemoved的NSNotifications。這總是發生。 - 爲什麼?

爲了使我使用的代碼完成,這裏是我的NSPersistentStoreCoordinatorStoresWillChangeNotification和NSPersistentStoreDidImportUbiquitousContentChangesNotification和一對夫婦等功能代碼:

- (void)storesWillChange:(NSNotification *)n { 

NSLog(@"storesWillChange"); 

NSArray *pStores = self.persistentStoreCoordinator.persistentStores; 
NSManagedObjectContext *moc = [self managedObjectContext]; 



switch ([[n.userInfo objectForKey:NSPersistentStoreUbiquitousTransitionTypeKey] integerValue]) { 

     case NSPersistentStoreUbiquitousTransitionTypeAccountAdded : { 

      NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountAdded"); 

      self.oldStore = pStores[0]; 

     } 

     break; 

     case NSPersistentStoreUbiquitousTransitionTypeAccountRemoved : 

      NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountRemoved"); 



      break; 

     case NSPersistentStoreUbiquitousTransitionTypeContentRemoved : 

      NSLog(@"NSPersistentStoreUbiquitousTransitionTypeContentRemoved"); 

      self.oldStore = pStores[0]; 

      break; 

     case NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted : 

      NSLog(@"NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted"); 

      NSLog(@"initial import"); 



      break; 

     default : 

      break; 

} 

[moc performBlockAndWait:^{ 

     NSError *error = nil; 

     if ([moc hasChanges]) { 

      [moc save:&error]; 

     } 

     [moc reset]; 

    }]; 


// reset user interface 


} 




- (void)persistentStoreDidImportUbiquitiousContentChanges:(NSNotification *)changeNotification { 

NSLog(@"*** Incoming iCloud Data ***"); 

NSManagedObjectContext *moc = [self managedObjectContext]; 



[moc performBlock:^{ 

     [moc mergeChangesFromContextDidSaveNotification:changeNotification]; 

    }]; 

} 




- (NSManagedObjectContext *)managedObjectContext { 

if (__managedObjectContext != nil) { 

     return __managedObjectContext; 

} 



NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 

if (coordinator != nil) { 

     __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 

//   __managedObjectContext = [[NSManagedObjectContext alloc] init]; 

     [__managedObjectContext setPersistentStoreCoordinator:coordinator]; 

     [__managedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy]; 

} 

return __managedObjectContext; 

} 


- (NSManagedObjectModel *)managedObjectModel { 

if (__managedObjectModel != nil) { 

     return __managedObjectModel; 

} 

NSURL *modelURL = [[NSBundle mainBundle] URLForResource:dataModelName withExtension:@"momd"]; 

__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; 

return __managedObjectModel; 

} 

所以基本上我要完成的是用戶有相同的數據,無論哪個iCloud帳戶即使是「本地」也在使用。

任何幫助,將不勝感激。先謝謝你!

+0

查看此鏈接,瞭解示例代碼和視頻,演示用戶更改其iCloud設置時iCloud同步和存儲遷移。 http://ossh.com.au/design-and-technology/software-development/ –

回答

2

從一個核心數據的存儲數據遷移到另一個(使用的iCloud與否是否)使用的NSPersistentStoreCoordinatormigratePersistentStore:toURL:options:withType:error:方法。

然而,確保您的數據始終是所有的iCloud賬戶之間相同的(遷移)是:

  • 可能是困難的(至少我不知道你會怎麼做)
  • 可能是你的用戶希望不在於它是有道理的,每個iCloud帳戶,有其自己的數據。 。

從iOS 7開始,iCloud Core Data得到了顯着改進,並且大大簡化了(針對開發人員)。其中一個簡化是,如果您打開無處不在的存儲但用戶未登錄到iCloud,則Core Data現在可以透明地處理iCloud帳戶更改和本地回退存儲,並創建「本地」存儲。

不幸的是,這個「本地」商店被視爲(虛擬)iCloud賬戶,因此從其遷移到另一個(真實)iCloud賬戶是棘手的。因此,最好在應用程序中使用「使用iCloud:是/否」選項。如果用戶選擇「否」,則創建一個真正的本地(不啓用iCloud)存儲(存儲在Documents文件夾中)。現在,當用戶更改應用程序中的iCloud選項時,您可以選擇將此本地存儲遷移到啓用iCloud的商店(使用migratePersistentStore:toURL:options:withType:error:),反之亦然(如果用戶從iCloud切換到本地)。

你問的其他問題是:

對於該用例,我必須使用NSMigratePersistentStoresAutomaticallyOption?這個選項如何工作?

選項NSMigratePersistentStoresAutomaticallyOptionNSInferMappingModelAutomaticallyOption用於商店遷移到更新的數據模型(例如,如果字段添加到實體),並沒有任何做遷移從一個商店到另一個。它們與您要解決的問題無關。

+0

當用戶禁用iCloud時,擁有真正的本地商店與「虛擬」iCloud帳戶有什麼好處? –

+0

@LocPham的好處是,無論iCloud是否啓用,您的應用程序都可以輕鬆訪問此非iCloud本地商店,從而允許用戶在選擇時從其遷移數據。如果用戶使用真實帳戶啓用iCloud,則無法從您的應用訪問「虛擬」iCloud帳戶存儲。這是因爲它被視爲屬於真正的iCloud帳戶,並且您的應用無法訪問其他iCloud帳戶的iCloud存儲。 – mluisbrown

-1

我建議不要在iOS6中使用iCloud + CoreData。有很多錯誤。閱讀:

http://www.theverge.com/2013/3/26/4148628/why-doesnt-icloud-just-work

http://www.zdnet.com/developers-give-apple-an-icloud-ultimatum-fix-it-by-june-7000013495/

編輯

在iOS7似乎已經有所改善,但開發商說,有問題時更改模型版本。 :(

+0

那是在iOS7之前。在iOS7中,iCloud同步效果很好。 – Shmidt

+0

對於iOS7,這確實已過時 –

+3

iCloud Sync在iOS 7中仍然會定期失敗,應儘可能避免。我現在大部分時間都是花時間排除核心數據iCloud問題,而不是處理新功能和/或應用程序,這仍然適用於iOS 7. –