managedObjectModel in nil ( only in WatchApp )

1k views Asked by At

I'm updating an app for Apple Watch This app use coredata , so i've create a framework to manage the Core Data stack ! When i run the app on device or simulator the app work fine , but when i run it on apple watch simulator the app crash with this log

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Cannot create an NSPersistentStoreCoordinator with a nil model'

The problem would seem to be the managedObjectModel and if i log it

NSLog(@"managedObjectModel %@", _managedObjectModel);

the log return

managedObjectModel (null)

the code in the framework seem right in fact the main app works perfectly

anyway this is the content of the framework

#import "DataAccess.h"

@implementation DataAccess

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;


+ (instancetype)sharedInstance
{
    static DataAccess *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[DataAccess alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

- (void)saveContext
{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}



#pragma mark - Core Data stack

// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

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

// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil) {

        NSLog(@"managedObjectModel %@", _managedObjectModel);

        return _managedObjectModel;

    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    NSLog(@"managedObjectModel %@", _managedObjectModel);


    return _managedObjectModel;
}

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }

    //NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"TMM.sqlite"];
    //NSURL *storeURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory  inDomains:NSAllDomainsMask] lastObject];
//    storeURL = [storeURL URLByAppendingPathComponent:@"db.sqlite"];

    NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.ragazzetto.MyApp.Documents"];
    storeURL = [storeURL URLByAppendingPathComponent:@"MyApp.sqlite"];

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {

        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _persistentStoreCoordinator;
}
#pragma mark - Application's Documents directory

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}


@end

Where is the problem ?

Thank you for your help

1

There are 1 answers

0
Tom Harrington On BEST ANSWER

When you write an iOS app extension-- including all current WatchKit apps-- you're creating a separate executable with its own bundle. Resources in the app are not necessarily available in the extension, and vice versa. So when you do this:

NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

The URL is different when running in the WatchKit app than it is in your containing app. The error tells you that the WatchKit app's URL is invalid, i.e. that there's no model file in the WatchKit app's bundle.

The simple fix is to just include the model in the WatchKit bundle. Do this by:

  1. Selecting the model file in Xcode
  2. Opening the file inspector panel on the right side of the Xcode window
  3. Look in the "target membership" section. Make sure your WatchKit target is selected.

That should work but will mean you have two copies of the model file. A better approach would be to put the model file into a shared framework that both targets use. That's a little more complex, but a little searching will find detailed steps.