• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: iCloud sync per app activation
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: iCloud sync per app activation


  • Subject: Re: iCloud sync per app activation
  • From: Martin Hewitson <email@hidden>
  • Date: Sun, 05 Feb 2012 10:43:26 +0100

OK, I've perhaps partially answered this myself, but I'd still be very happy to hear any opinions on this. Below is a sample of code which handles the main bits of my solution (error handling etc removed).

Does this look reasonable? Still it doesn't handle the question: how to deal with changes that occurred locally while iCloud syncing was disabled?

Cheers,

Martin

On 5, Feb, 2012, at 09:43 AM, Martin Hewitson wrote:

>>>
>>
>> I'm finishing up a shoebox app now and I do have the option to store things in iCloud or not. My eventual solution to this was to have a preference screen in the app with a single "enable iCloud' switch. If you flip it from off to on, or on to off, you get a section of buttons to hit asking how you want to perform the transition (eg when transitioning to the cloud you can merge local to cloud, use cloud or use local), it then gives you a confirmation box before you do it. I failed to find a really good way to do this in preferences, so I put it in the app itself, there are just too many questions about how you want to perform the migration which I think need to be asked then.
>>
>
> Hi Roland,
>
> I'm starting to think about how to implement this in my OS X app. Would you be willing to share any clues as the correct strategies? How to merge the managed object contexts? How to make sure that changes accumulated locally while not syncing with iCloud are then transferred to the cloud? If I want to replace the 'truth' in the cloud with the local store, how do I make sure that all the necessary transactions exist so that other clients update themselves?
>
> The reason I'm worried about the correct way to do this is due to the following scenarios:
>
> 1) Starting from a completely new app
>
> Here I've been able to configure and sync a persistent store as long as I have the ubiquity keys in the store options the very first time the app runs. If I want to change the 'truth' in the cloud, or start from a fresh persistent store, I haven't found a reliable way to do that. Deleting the local store doesn't work for me. Essentially the only thing that works is the solution in point 2) below.
>
> 2) Starting from an existing app with an existing persistent store.
>
> One thing I've noticed (at least on OS X) is that if I have an existing core data store which was previously being used without iCloud, and then I add the iCloud ubiquity keys to the store options, the contents of the store (the full sql data store) are not pushed to the cloud container when the app starts. In order to get this to work I've need to use -migratePersistentStore:toURL:options:withType:error: to move the existing store to a new URL. Then the full store is uploaded and everything works from there.
>
> So, switching between these two states (with a user option) seems tricky to me. At least I can't see a good way to handle.
>


- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
  if (__persistentStoreCoordinator) {
    return __persistentStoreCoordinator;
  }

  NSManagedObjectModel *mom = [self managedObjectModel];
  if (!mom) {
    return nil;
  }

  NSURL *applicationFilesDirectory = [self applicationSupportDirectory];
  NSPersistentStoreCoordinator *coordinator = [[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom] autorelease];

  // Store URL
  NSString *storeName    = @"MyApp.sqlstoredata";
  NSURL *storeURL = [applicationFilesDirectory URLByAppendingPathComponent:storeName];

  // Ubiquity container
  NSString *containerID  = @"...myid...";
  NSURL *contentURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:containerID];
  NSLog(@"iCloud content URL: %@", contentURL);

  // store options
  NSMutableDictionary *options = [[[NSMutableDictionary alloc] init] autorelease];
  if (contentURL != nil && [self shouldSyncWithiCloud]) {
    [options setObject:storeName forKey:NSPersistentStoreUbiquitousContentNameKey];
    [options setObject:contentURL forKey:NSPersistentStoreUbiquitousContentURLKey];
  }

  error = nil;
  NSPersistentStore *store = [coordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                       configuration:nil
                                                                 URL:storeURL
                                                             options:options
                                                               error:&error];

  __persistentStoreCoordinator = [coordinator retain];
  return __persistentStoreCoordinator;
}

- (NSManagedObjectContext *)managedObjectContext
{
  if (__managedObjectContext) {
    return __managedObjectContext;
  }

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

  if ([self shouldSyncWithiCloud]) {
    __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [__managedObjectContext performBlockAndWait:^{
      [__managedObjectContext setPersistentStoreCoordinator: coordinator];
      [__managedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
    }];
  } else {
    __managedObjectContext = [[NSManagedObjectContext alloc] init];
    [__managedObjectContext setPersistentStoreCoordinator: coordinator];
  }

  return __managedObjectContext;
}

// An action triggered by a check box
- (IBAction)iCloudSyncStateAction:(id)sender
{
  if ([self shouldSyncWithiCloud]) {
    // We weren't syncing before but we are now. So we need to ask the user if they want to merge
    // the data from iCloud
    NSAlert *alert = [NSAlert alertWithMessageText:@"Do you want to merge your trips with iCloud?"
                                     defaultButton:@"Merge"
                                   alternateButton:@"Cancel"
                                       otherButton:nil
                         informativeTextWithFormat:@"Your trips on this Mac will be uploaded and merged with the trips stored in iCloud."];
    [alert beginSheetModalForWindow:self.window
                      modalDelegate:self
                     didEndSelector:@selector(mergeWithiCloudAlertDidEnd:returnCode:contextInfo:)
                        contextInfo:NULL];
  } else {
    // we were syncing and now we're not
    [self restartManagedObjectContext];
  }

}

- (void) mergeWithiCloudAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
{
  if (returnCode == NSAlertAlternateReturn) {
    // cancel
    // set sync state back to NO
    [self setShouldSyncWithiCloud:NO];
    return;
  }

  // save any changes we have currently
  NSError *error = nil;
  if (![[self managedObjectContext] commitEditing]) {
    NSLog(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd));
  }
  if (![[self managedObjectContext] save:&error]) {
    [[NSApplication sharedApplication] presentError:error];
    return;
  }

  // restart the MOC and PSC
  [self restartManagedObjectContext];
}

- (void) restartManagedObjectContext
{
  [self willChangeValueForKey:@"managedObjectContext"];

  [__managedObjectContext release];
  __managedObjectContext = nil;
  [__persistentStoreCoordinator release];
  __persistentStoreCoordinator = nil;

  // just to force the moc to be recreated
  [self managedObjectContext];

  [self didChangeValueForKey:@"managedObjectContext"];
}

- (void)setShouldSyncWithiCloud:(BOOL)state
{
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  [defaults setValue:[NSNumber numberWithBool:state] forKey:MHSyncWithiCloud];
  [defaults synchronize];
}

- (BOOL)shouldSyncWithiCloud
{
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  return [[defaults valueForKey:MHSyncWithiCloud] boolValue];
}


_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden


References: 
 >iCloud sync per app activation (From: Martin Hewitson <email@hidden>)
 >Re: iCloud sync per app activation (From: Roland King <email@hidden>)
 >Re: iCloud sync per app activation (From: Martin Hewitson <email@hidden>)

  • Prev by Date: Re: Is slowing down bindings updates possible?
  • Next by Date: Re: TextEdit - Open Recent - slow
  • Previous by thread: Re: iCloud sync per app activation
  • Next by thread: Re: Recently Opened in Doc
  • Index(es):
    • Date
    • Thread