iCloud核心数据迁移

时间:2013-10-17 16:43:24

标签: ios core-data icloud

我正在开发一个仅限iOS 7的应用程序,而且我对iCloud和Core数据还不熟悉。 我的假设是,如果可能,用户总是希望使用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;

}

使用此设置,即使用户未登录iCloud(使用“local”文件夹),我也会立即开始使用Documents / CoreDataUbiquitySupport下的文件夹。

如您所见,我正在注册通知。

  • 为什么在NSPersistentStoreCoordinatorStoresWillChangeNotification和NSPersistentStoreCoordinatorStoresDidChangeNotification的NSNotifications中的密钥NSAddedPersistentStoresKey和NSRemovedPersistentStoresKey的数组中添加和删除的持久性存储总是相同的,例如,为案例帐户添加? 可以预期通知包含已删除的持久性存储和新添加的。所以他们不应该是一样的。但不幸的是,它们总是一样的。

目前的行为是,如果我切换帐户或退出帐户或登录到新帐户,应用程序始终以相关商店启动,如果以前没有,则应启动新应用商店。我希望的行为是应用程序始终将数据迁移到新商店。例如:用户未登录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且应用程序正在运行且用户将应用程序带到后台然后返回到前台时,NSPersistentStoreCoordinatorStoresWillChangeNotification和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帐户,即使它是“本地的”。

任何帮助将不胜感激。提前谢谢!

2 个答案:

答案 0 :(得分:2)

要将数据从一个Core Data存储迁移到另一个(无论是否使用iCloud),请使用migratePersistentStore:toURL:options:withType:error:的{​​{1}}方法。

但是,确保所有iCloud帐户之间的数据始终相同(已迁移):

  • 可能很难(至少我不知道你是怎么做的)。
  • 可能不是您的用户想要的。每个iCloud帐户都有自己的数据是有意义的。

从iOS 7开始,iCloud Core Data得到了显着改进,并且大大简化了(对于开发人员而言)。其中一个简化是Core Data现在透明地处理iCloud帐户更改和本地回退存储,并且如果您打开无处不在的商店但用户未登录iCloud,则创建“本地”商店。

不幸的是,这个“本地”商店被视为一个(虚拟)iCloud帐户,因此从它迁移到另一个(真正的)iCloud帐户是棘手的。因此,最好在您的应用中使用“使用iCloud:是/否”选项。如果用户选择“否”,则创建一个真正的本地(不支持iCloud)存储(存储在NSPersistentStoreCoordinator文件夹中)。现在,当用户更改应用程序中的iCloud选项时,您可以选择将此本地存储迁移到启用iCloud的存储(使用Documents),反之亦然,如果用户从iCloud切换到本地。

你问的另一个问题是:

  

我必须使用NSMigratePersistentStoresAutomaticallyOption来使用哪个用例?该选项如何正常工作?

选项migratePersistentStore:toURL:options:withType:error:NSMigratePersistentStoresAutomaticallyOption用于将商店迁移到更新的数据模型(例如,如果您向实体添加字段)并且与之无关从一个商店迁移到另一个商店。它们与您想要解决的问题无关。

答案 1 :(得分:-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中似乎有所改善,但开发人员说在更改模型版本时会出现问题。 :(