3

I've looked at every similar question I can find and none of their solutions are working for me.

One issue might be that the addition of entities to the context (which succeeds) and the saving of the context happen on a different thread after I get JSON back and parsed from a webAPI. But the context is setup the first time the manageContext and persistent store are used, as you can see below. So it would be on that thread after the parsing where this is happening.

The exact error:

 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This NSPersistentStoreCoordinator has no persistent stores.  It cannot perform a save operation.'

I've tried deleting the app from the simulator, as that was suggested, didn't change.

Here's my CoreDataHelper class that I'm using, and there's only 1 instance of it in my app, the error happens when I call saveContext method on the helper after adding new items to the context:

@implementation CoreDataHelper

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _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];
}

- (void)saveContext
{
    NSError *error = nil;
    if (self.managedObjectContext != nil) {
        if ([self.managedObjectContext hasChanges] && ![self.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();
        }
    }
}

- (void)dealloc {

    [self saveContext];
}

#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) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    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:@"UserGroupTV.sqlite"];

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

    // needed for lightweight migrations
    NSMutableDictionary *options = [NSMutableDictionary dictionary];
    [options setObject:[NSNumber numberWithBool:YES]
                forKey:NSMigratePersistentStoresAutomaticallyOption];
    [options setObject:[NSNumber numberWithBool:YES]
                forKey:NSInferMappingModelAutomaticallyOption];

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

        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error"
                                                            message:[error localizedDescription]
                                                           delegate:self
                                                  cancelButtonTitle:@"OK"
                                                  otherButtonTitles:nil, nil];
        [alertView show];
    }

    return _persistentStoreCoordinator;
}


@end

ETA: I've seen it mentioned, but how/where do I delete the local folder on the machine that stores the sql file?

Mark W
  • 3,879
  • 2
  • 37
  • 53

2 Answers2

3

I don't have a clear cut answer but here's a few things to look at:

  1. Put some logging in where you create the persistentStoreCoordinator. If an error occurs adding the store during creation you can catch it there.

  2. You mentioned that you are saving the managed object context on a background thread. NSManagedObjectContext is not thread safe and you should only use a specific context on the thread it was created on. You need to have, at minimum, one context per thread. You can observe 'NSManagedObjectContextDidSaveNotification' and merge changes into your main context.

XJones
  • 21,959
  • 10
  • 67
  • 82
  • There's code in there that if there is an error (the return value is nil for the store) when I create the persistentStoreCoord it will pop up an alert view. It's not happening. – Mark W Apr 16 '13 at 20:11
  • Checking for a nil return doesn't catch this case. It's valid to create an `NSPersistentStoreCoordinator` with no stores. You need to check if your specific store was added. It is likely not. Also, the only time I ran into this error was when my app was being run in the background. May not be your issue but see http://stackoverflow.com/questions/12845790/how-to-debug-handle-intermittent-authorization-denied-and-disk-i-o-errors-wh. – XJones Apr 16 '13 at 21:00
0

Turns out I was deleting the store and not recreating it every time I recalled the web API. My original goal was to clean out the database every time a user wanted to refresh the list of videos by calling the api, and I was deleting all objects, then deleting the store and forgetting to recreate it. Here was my refresh data base scheme, I simply removed everything below the saveContext call.

- (void)resetDataBase {

    [self deleteAllObjects:@"Speaker"];
    [self deleteAllObjects:@"Tag"];
    [self deleteAllObjects:@"UserGroup"];
    [self deleteAllObjects:@"Video"];

    [_coreDataHelper saveContext];

    // REMOVED BELOW and it worked
    NSPersistentStore * store = [[_coreDataHelper.persistentStoreCoordinator persistentStores] lastObject];
    if (store) {
        NSError * error;
        [_coreDataHelper.persistentStoreCoordinator removePersistentStore:store error:&error];
        if (error) {
            [self showError:error];
        }
    }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"UserGroupTV.sqlite"];
    [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
}
Mark W
  • 3,879
  • 2
  • 37
  • 53