3

I enabled iCloud in my iOS app and on first launch of the app the app freezes for about 5 seconds when i press on any view in the app. I followed this tutorial to enable iCloud in my app and synchronize data using core data. I did not have this problem before adding iCloud synchronization. This only happens on first launch, not on other launches of the app. iCloud synchronization does occur. The problem is the app freezing.

Here is the code that manages the synchronization in my app delegate.

- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    /*if (persistentStoreCoordinator == nil) {
        NSURL *storeURL = [NSURL fileURLWithPath:[self dataStorePath]];

        persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];

        NSError *error;
        if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
            NSLog(@"Error adding persistent store %@, %@", error, [error userInfo]);
            abort();
        }
    }
    return persistentStoreCoordinator;*/

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    if ((persistentStoreCoordinator != nil)) {
        return persistentStoreCoordinator;
    }

    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    NSPersistentStoreCoordinator *psc = persistentStoreCoordinator;

    // Set up iCloud in another thread:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // ** Note: if you adapt this code for your own use, you MUST change this variable:
        NSString *iCloudEnabledAppID = @"48S27G4A2S.com.maxned.iDownloadBlog";

        // ** Note: if you adapt this code for your own use, you should change this variable:
        NSString *dataFileName = @"DataStore.sqlite";

        // ** Note: For basic usage you shouldn't need to change anything else

        NSString *iCloudDataDirectoryName = @"Data.nosync";
        NSString *iCloudLogsDirectoryName = @"Logs";
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSURL *localStore = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:dataFileName];
        NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier:nil];

        if (iCloud)
        {
            //NSLog(@"iCloud is working");

            NSURL *iCloudLogsPath = [NSURL fileURLWithPath:[[iCloud path] stringByAppendingPathComponent:iCloudLogsDirectoryName]];

            /*NSLog(@"iCloudEnabledAppID = %@",iCloudEnabledAppID);
            NSLog(@"dataFileName = %@", dataFileName);
            NSLog(@"iCloudDataDirectoryName = %@", iCloudDataDirectoryName);
            NSLog(@"iCloudLogsDirectoryName = %@", iCloudLogsDirectoryName);
            NSLog(@"iCloud = %@", iCloud);
            NSLog(@"iCloudLogsPath = %@", iCloudLogsPath);*/

            if ([fileManager fileExistsAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]] == NO) {
                NSError *fileSystemError;
                [fileManager createDirectoryAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]
                       withIntermediateDirectories:YES
                                        attributes:nil
                                             error:&fileSystemError];

                if (fileSystemError != nil) {
                    NSLog(@"Error creating database directory %@", fileSystemError);
                }
            }

            NSString *iCloudData = [[[iCloud path]
                                     stringByAppendingPathComponent:iCloudDataDirectoryName]
                                    stringByAppendingPathComponent:dataFileName];

            //NSLog(@"iCloudData = %@", iCloudData);

            NSMutableDictionary *options = [NSMutableDictionary dictionary];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
            [options setObject:iCloudEnabledAppID forKey:NSPersistentStoreUbiquitousContentNameKey];
            [options setObject:iCloudLogsPath forKey:NSPersistentStoreUbiquitousContentURLKey];

            [psc lock];

            [psc addPersistentStoreWithType:NSSQLiteStoreType
                configuration:nil
                URL:[NSURL fileURLWithPath:iCloudData]
                options:options
                error:nil];

            [psc unlock];

        } else {

            NSLog(@"iCloud is NOT working - using a local store");
            NSMutableDictionary *options = [NSMutableDictionary dictionary];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];

            [psc lock];

            [psc addPersistentStoreWithType:NSSQLiteStoreType
                configuration:nil
                URL:localStore
                options:options
                error:nil];

            [psc unlock];

        }

        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:@"SomethingChanged" object:self userInfo:nil];
        });
    });

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

    return persistentStoreCoordinator;
}

/*- (NSManagedObjectContext *)managedObjectContext
{
    if (managedObjectContext == nil) {
        NSPersistentStoreCoordinator *coordinator = self.persistentStoreCoordinator;
        if (coordinator != nil) {
            managedObjectContext = [[NSManagedObjectContext alloc] init];
            [managedObjectContext setPersistentStoreCoordinator:coordinator];
        }
    }
    return managedObjectContext;
}*/

- (NSManagedObjectContext *)managedObjectContext
{
    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

    if (coordinator != nil) {
        NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

        [moc performBlockAndWait:^{
            [moc setPersistentStoreCoordinator: coordinator];
            [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:coordinator];
        }];
        managedObjectContext = moc;
    }

    return managedObjectContext;
}

- (void)mergeChangesFrom_iCloud:(NSNotification *)notification
{
    //NSLog(@"Merging in changes from iCloud...");

    NSManagedObjectContext* moc = [self managedObjectContext];

    [moc performBlock:^{

        [moc mergeChangesFromContextDidSaveNotification:notification];

        NSNotification* refreshNotification = [NSNotification notificationWithName:@"SomethingChanged"
            object:self
            userInfo:[notification userInfo]];

        [[NSNotificationCenter defaultCenter] postNotification:refreshNotification];
    }];
}
maxned
  • 393
  • 2
  • 16

3 Answers3

3

To fix this problem I changed the code to put it in another thread.

dispatch_queue_t mainQueue = dispatch_get_main_queue();
        dispatch_async(mainQueue, ^{

            NSFileManager *fileManager = [NSFileManager defaultManager];
            NSURL *localStore = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:dataFileName];
            NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier:nil];

            if (iCloud)
            {
                //NSLog(@"iCloud is working");

                NSURL *iCloudLogsPath = [NSURL fileURLWithPath:[[iCloud path] stringByAppendingPathComponent:iCloudLogsDirectoryName]];

                /*NSLog(@"iCloudEnabledAppID = %@",iCloudEnabledAppID);
                 NSLog(@"dataFileName = %@", dataFileName);
                 NSLog(@"iCloudDataDirectoryName = %@", iCloudDataDirectoryName);
                 NSLog(@"iCloudLogsDirectoryName = %@", iCloudLogsDirectoryName);
                 NSLog(@"iCloud = %@", iCloud);
                 NSLog(@"iCloudLogsPath = %@", iCloudLogsPath);*/

                if ([fileManager fileExistsAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]] == NO) {
                    NSError *fileSystemError;
                    [fileManager createDirectoryAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]
                           withIntermediateDirectories:YES
                                            attributes:nil
                                                 error:&fileSystemError];

                    if (fileSystemError != nil) {
                        NSLog(@"Error creating database directory %@", fileSystemError);
                    }
                }

                NSString *iCloudData = [[[iCloud path]
                                         stringByAppendingPathComponent:iCloudDataDirectoryName]
                                        stringByAppendingPathComponent:dataFileName];

                //NSLog(@"iCloudData = %@", iCloudData);

                NSMutableDictionary *options = [NSMutableDictionary dictionary];
                [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
                [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
                [options setObject:iCloudEnabledAppID forKey:NSPersistentStoreUbiquitousContentNameKey];
                [options setObject:iCloudLogsPath forKey:NSPersistentStoreUbiquitousContentURLKey];

                [psc lock];

                [psc addPersistentStoreWithType:NSSQLiteStoreType
                                  configuration:nil
                                            URL:[NSURL fileURLWithPath:iCloudData]
                                        options:options
                                          error:nil];

                [psc unlock];

            } else {

                NSLog(@"iCloud is NOT working - using a local store");
                NSMutableDictionary *options = [NSMutableDictionary dictionary];
                [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
                [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];

                [psc lock];

                [psc addPersistentStoreWithType:NSSQLiteStoreType
                                  configuration:nil
                                            URL:localStore
                                        options:options
                                          error:nil];

                [psc unlock];
            }
        });
maxned
  • 393
  • 2
  • 16
  • It is in another thread. I figured out the problem now. It was because I was trying to access iCloud before it was ready. – maxned Feb 10 '13 at 18:59
0

If this code is running on the main thread, that might be your problem.

From where is this code invoked on first (or any) launch?

Mark Granoff
  • 16,878
  • 2
  • 59
  • 61
  • it is invoked in managedObjectContext: if you look, the code in persistentStoreCoordinator is running in a second thread – maxned Oct 16 '12 at 21:17
0

Doing some searching myself, looks like this is actually expected behavior. As others have said App killed by SIGKILL when changing privacy settings and here: https://devforums.apple.com/message/715855

It is poorly documented, but working as designed. "On any of the permission changes iOS will terminate your app so that you aren't operating under the assumption of whatever the permissions were before. For example lets say the user gave you permissions for Contacts. Then they background your app and go and change that. You'd operate under the idea that you have permission when now you don't. So iOS just terminates the app."

"But it does not crash it is just forced to restart. You will get a SIGKILL message but no Crash log."

Community
  • 1
  • 1
skinsfan00atg
  • 571
  • 1
  • 3
  • 19