Question

currently i try to establish iCloud with CoreData. My app is 'only' iOS 7 based, so it is quite easy to enable iCloud. The basics are working very well (Saving to iCloud). I was searching for over two weeks now, but i didn't find a solution for my problem.

My problem: If the user enable/disable iCloud in the settings or change the iCloud account my app should merge the changes in both ways. So here are my scenarios:

App starts -> iCloud is OFF -> Data is saved (local) -> User turns iCloud ON -> local data will be merged to iCloud

App starts -> iCloud is ON -> Data is saved (iCloud) -> User turns iCloud OFF -> iCloud data will be merged to local

In many other threads some developers wrote, that it is enough to have only ONE persistent store with same options and url, because since iOS 7 core data handles an automatic fallback store if iCloud is disabled and trigger the merge if it is available again. But that doesn't work for me.

Here are some code snippets:

- (NSURL*)storeURL{
NSURL* documentsDirectory = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
return [documentsDirectory URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.sqlite",coreDataFileName]];
}

- (NSURL*)modelURL{
return [[NSBundle mainBundle] URLForResource:coreDataFileName withExtension:@"momd"];
}

-(NSMutableDictionary*)getICloudPersistentStoreOptions{
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES] forKey:
 NSInferMappingModelAutomaticallyOption];

//if(self.hasICloudAccountOnDevice & self.iCloudLokalIsAvailable){
[options setObject:@"iCloudStore" forKey:NSPersistentStoreUbiquitousContentNameKey];        
//}
return options;
}


- (NSManagedObjectContext *)managedObjectContext{
if (masterContext != nil){
    return masterContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil){
    masterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    masterContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
    [masterContext setPersistentStoreCoordinator: coordinator];
}
return masterContext;
}

- (NSManagedObjectModel *)managedObjectModel{
if (managedObjectModel != nil){
    return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:self.modelURL];
return managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{
if((persistentStoreCoordinator != nil)){
    return persistentStoreCoordinator;
}
[self createPersistentStoreCoordinator];
return persistentStoreCoordinator;
}

-(void)createPersistentStoreCoordinator{
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSPersistentStoreCoordinator *psc = persistentStoreCoordinator;

// iCloud notification subscriptions
NSNotificationCenter *dc = [NSNotificationCenter defaultCenter];
[dc addObserver:self
       selector:@selector(storesWillChange:)
           name:NSPersistentStoreCoordinatorStoresWillChangeNotification
         object:psc];

[dc addObserver:self
       selector:@selector(storesDidChange:)
           name:NSPersistentStoreCoordinatorStoresDidChangeNotification
         object:psc];

[dc addObserver:self
       selector:@selector(persistentStoreDidImportUbiquitousContentChanges:)
           name:NSPersistentStoreDidImportUbiquitousContentChangesNotification
         object:psc];

// Set up iCloud persistent store in another thread:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    NSError* error;
    [psc lock];
    [psc addPersistentStoreWithType:NSSQLiteStoreType
                      configuration:nil
                                URL:self.storeURL
                            options:[self getICloudPersistentStoreOptions]
                              error:&error];
    [psc unlock];
});
}

in the 'storeWillChange' method i want to implement some deduplication code after NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted but thats another thing.

NSPersistentStoreDidImportUbiquitousContentChangesNotification is currently not used (i think it is only called if an other device saves also some data to iCloud .

And 'storeDidChange' from NSPersistentStoreCoordinatorStoresDidChangeNotification is called on every app start. So currently i no idea what i should do there.

As i wrote before this doesn't work for me, if i enable or disable iCloud the data won't be merged.

Please help me - Are my thoughts wrong??

Was it helpful?

Solution

Have a look here, I made some notes on the issues. http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/sample-apps-explanations/

Also take a look at the sample apps that handle iCloud account transitions, I think you can choose a merge option when a transition is required and a destination store already exists.

Note that the iCloud store is removed if the user selects to use a local store while ICloud is ON. Also not that no attempt is made to migrate an iCloud store after iCloud has been turned off. According to Apple you can't rely on the iCloud store being available once the user logs out of iCloud.

OTHER TIPS

There are two ways to do that:

  1. Deep copy entities from one store to another;
  2. Migrate store from local to iCloud or vice versa.

The second way is much easier due to less code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top