Search code examples
objective-ccore-dataapple-watchtoday-extension

How to re-use Core Data in Extension code in Objective-C? The managedObjectContext can't be created by UIApplicationDelegate as we use to do


The problem I meet is we can't get the managedObjectContext by this way:

 [((MDAppDelegate*)appController) mainQueueContext] ; 

Because the error message is:

'sharedApplication' is unavailable: not available on iOS (App Extension) - Use view controller based solutions where appropriate instead.

My QUESTION is:
Is there any existing example to help us connect to Core Data through Extension (Today/Watch)?

P.S. I have read following questions, none of them help. I just need an example:
App and Extension - Use Core data == error : sharedApplication()' is unavailable
WatchKit : 'sharedApplication' is unavailable: not available on iOS (App Extension) - Use view controller based solutions where appropriate instead
Use AppDelegate in today extension


Solution

  • We found and fix the problem now. The problem is caused by my misunderstanding of Core Data. We used to re-use tutorial's source code. When the system is work, we have no time to get deep understand of it.

    The problem is we can't use Container App's managed object context. To fix it we use following code: replace

    [((MDAppDelegate*)appController) mainQueueContext] ;

    to

    [self mainQueueContext] ;

    Then, add following...

    - (NSManagedObjectContext *)mainQueueContext {
        if (_mainQueueContext != nil) {
            return _mainQueueContext;
        }
        _mainQueueContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_mainQueueContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
        [_mainQueueContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
        return _mainQueueContext; }
    
    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
    {
        if (_persistentStoreCoordinator != nil) {
            return _persistentStoreCoordinator;
        }
        NSURL *storeURL;
        NSString *containerPath = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.<your project group ID>"].path;
        NSString *sqlitePath = [NSString stringWithFormat:@"file://%@/%@", containerPath, @"<Your database file>.sqlite"];
        storeURL = [NSURL URLWithString:sqlitePath];
        NSError *error = nil;
        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
        NSDictionary *options = @{
                                  NSMigratePersistentStoresAutomaticallyOption : @YES,
                                  NSInferMappingModelAutomaticallyOption : @YES
                                  };
        if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                       configuration:nil
                                                                 URL:storeURL
                                                             options:options
                                                               error:&error]) {
            abort();
        }
        NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
        if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:storeURL.path error:&error]) {
            // Handle error
        }
        return _persistentStoreCoordinator;
    }
    
    - (NSManagedObjectModel *)managedObjectModel
    {
        if (_managedObjectModel != nil) {
            return _managedObjectModel;
        }
        NSURL *modelURL;
        NSString *containerPath = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.<your project group ID>"].path;
        NSString *modelPath = [NSString stringWithFormat:@"file://%@/%@", containerPath, @"<Your database file>.momd"];
        modelURL = [NSURL URLWithString:modelPath];
        _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        return _managedObjectModel;
    }
    

    Another Important issue is the Container App is still using the Core Data store located in application's Documents directory. The extension can't access it. So, we migrate the store to group shared folder using below lines:

        NSString *directoryShared = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.<your project group ID>"].path;
        NSString *storePathShared = [NSString stringWithFormat:@"file://%@/%@", directoryShared, @"<Your database file>.sqlite"];
        NSURL *storeUrlShared = [NSURL URLWithString:storePathShared];
        [_persistentStoreCoordinator migratePersistentStore:store
                                                      toURL:storeUrlShared
                                                    options:options
                                                   withType:NSSQLiteStoreType
                                                      error:&error];
        if (error != nil) {
            NSLog(@"Error when migration to groupd url %@, %@", error, [error userInfo]);
        }
    

    We just leave the original database alone, let the container app keep using it. there are some better way to achieve it. like: Migrating NSPersistentStore from application sandbox to shared group container