Question

Is it possible to intercept if the user switches from on to off the iCloud support under Settings -> iCloud -> Document & Data?

Obviously when he does this the app has already resigned active and entered the background. I am targeting iOS7 and I would like to keep in sync the UIManagedDocument, otherwise it would be like having two different UIDocuments: one with iCloud support and all the data created until the switch from on to off and a new one without any data in it. If I create data when iCloud support has been switched to off, and then I switch back to on I get the same DB I had when the support has been switched to off.

Note: I believe nelico's answer is right. He wrote: "If your app is running and the user changes enables or disable Document & Data iCloud syncing via the settings app, your app will receive the SIGKILL signal."

When the user changes the settings the app is ALREADY in the background and receives the SIGKILL signal. This is what I do not understand and I do not want. Registering for NSUbiquityIdentityDidChangeNotification doesn't solve this problem.

Was it helpful?

Solution

Another, cleaner, solution is to listen for the NSUbiquityIdentityDidChangeNotification notification and when you get that notification then check the URLForUbiquityContainerIdentifier if it is null they either signed out or turned off 'Documents and Data'. You should also be tracking the current ubiquity token so that you can know not only if they logged off but if they changed iCloud accounts. It happens more than one might think because Apple Geniuses like to just create a new iCloud account when things go wrong on users devices.

ex:

id <NSObject,NSCopying,NSCoding> _currentUbiquityIdentityToken;

...

_currentUbiquityIdentityToken = [[NSFileManager defaultManager] ubiquityIdentityToken];
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector (_iCloudAccountAvailabilityChanged:) name: NSUbiquityIdentityDidChangeNotification object: nil];

...

- (void)_iCloudAccountAvailabilityChanged:(NSNotification*)notif {
    if (![_currentUbiquityIdentityToken isEqual:[[NSFileManager defaultManager] ubiquityIdentityToken]]) {
        // Update the current token and rescan for documents.
        _currentUbiquityIdentityToken = [[NSFileManager defaultManager] ubiquityIdentityToken];
        // Do something about the change here...
    }
}

OTHER TIPS

Usually you would put the checks in the method below. That way whenever the app becomes active (including first time its launched) you can check the settings just prior to returning control to the user, no need to do polling in the background. For UIManagedDocument you may want to migrate the store to a local only copy and remove any iCloud content or the opposite, depending on the users input.

BTW just include the check in the previous answer to test if the global iCloud settings have been turned on or off. I don't do this because its only necessary to change the apps behaviour if the user has set the app specific settings.

/*! The app is about to enter foreground so use this opportunity to check if the user has changed any
    settings.  They may have changed the iCloud account, logged into or out of iCloud, set Documents & Data to off (same effect as
    if they logged out of iCloud) or they may have changed the app specific settings.
    If the settings have been changed then check if iCloud is being turned off and ask the user if they want to save the files locally.
    Otherwise just copy the files to iCloud (don't ask the user again, they've just turned iCloud on, so they obviously mean it!)

 @param application The application
 */
- (void)applicationWillEnterForeground:(UIApplication *)application
{
    //LOG(@"applicationWillEnterForeground called");

    // Check if the settings have changed
    [[NSUserDefaults standardUserDefaults] synchronize];
    NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
    bool userICloudChoice = [userDefaults boolForKey:_cloudPreferenceKey];

    // Check against the current in memory setting
    if (userICloudChoice  == useICloudStorage) {

        // The setting has not been changed so just ignore
        //LOG(@" iCloud choice has not changed");


    } else {

        // The setting has changed so do something
        //LOG(@" iCloud choice has been changed!!");

        // iCloud has been turned off so ask the user if they want to keep files locally
        if (!userICloudChoice) {
            //LOG(@" Ask user if  they want to keep iCloud files ?");

            if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
                _cloudChangedAlert = [[UIAlertView alloc] initWithTitle:@"You're not using iCloud" message:@"What would you like to do with documents currently on this phone?" delegate:self cancelButtonTitle:@"Keep using iCloud" otherButtonTitles:@"Keep on My iPhone", @"Delete from My iPhone", nil];
            } else {
                _cloudChangedAlert = [[UIAlertView alloc] initWithTitle:@"You're not using iCloud" message:@"What would you like to do with documents currently on this phone?" delegate:self cancelButtonTitle:@"Keep using iCloud" otherButtonTitles:@"Keep on My iPad", @"Delete from My iPad", nil];

            }

            [_cloudChangedAlert show];

        } else {

            // iCloud has been turned on so just copy the files across, don't ask the user again...

            //LOG(@" iCloud turned on so copy any created files across");
            [[CloudManager sharedManager] setIsCloudEnabled:YES];  // This does all the work based on the settings passed to it
            useICloudStorage = YES;

        }
    }

}

Oh and also register for this notification in case the user logs on using another iCloud account.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkUserICloudPreferenceAndSetupIfNecessary) name:NSUbiquityIdentityDidChangeNotification object:nil];

It's not possible to intercept the changes, but you can programmatically check to see if they have iCloud enabled:

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *ubiquityContainerURL = [fileManager URLForUbiquityContainerIdentifier:nil];
if (!ubiquityContainerURL) {
    // iCloud is not enabled
}

When your app enters the background, you could periodically poll this and have your app respond to any change in state.

EDIT: If your app is running and the user changes enables or disable Document & Data iCloud syncing via the settings app, your app will receive the SIGKILL signal. However, the OS can terminate your app for a number of reasons so trapping SIGKILL is not a reliable method of intercepting iCloud sync setting changes. You are still better off periodically polling.

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