Randoramma
Randoramma

Reputation: 133

iOS Core Data saveContext method is unsuccessful due to NSSQLiteErrorDomain = 1032

Some background for this issue, I'm trying to include what I think may be relevant to help understand the context.

I am currently adding an linked library which used Core Data to save some user information and a feature which adds an Entity to the pre-existing Core Data model already in the app. Each managedObjectContext has its own instance when created (verified) as well as its own PSC and MOM and neither interact with the other's entities(thus seem to be independent).

The entirety of the following code, errors, and (I believe issue) is in the Main Target of the app. (Hopefully) not the newly added linked library.

The saveContext method is:

- (void)saveContext {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSError *error = nil;
        // Register
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myManagedObjectContextDidSaveNotificationHandler:) name:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];

        if (self.managedObjectContext != nil) {
            if ([self.managedObjectContext hasChanges]) {
                BOOL success = [self.managedObjectContext save:&error];
                if (!success) {
                    [Error showErrorByAppendingString:NSLocalizedString(@"UnableToSaveChanges", nil) withError:error];
                } else {
                     // 
                }
            }
        }
        // Unregister
        [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification    object:self.managedObjectContext];
    });  
}

When called, error = nil, success = NO and by forcing the compiler past the exception I get the following:

CoreData: error: exception during obtainPermenantIDsForObjects: Updating max pk failed: attempt to write a readonly database with userInfo of { NSSQLiteErrorDomain = 1032; }

I have googled, "NSSQLiteErrorDomain = 1032", "obtainPermenantIDsForObjects", and "CoreData readonly database". It does appear that the key primary key for each object is the same, but I am setting that value, I believe sqlite is. I have not found any solutions to help with this. I do have the argument passed on launch, "Concurrency Debug 1" set to on.

I have not implemented obtainPermenantIDsForObjects and I've searched the whole project and cant find its implementation so I think CoreData is using this.

The saveContext method is called on the main queue because thats how my predecessors rolled out the code and I don't have time at the moment to deal with it.

The method calling saveContext (from a background thread):

- (NSMutableArray *)convertRawStepDataTo:(NSMutableArray*)steps
                           withDates:(NSMutableArray*)dates
              inManagedObjectContext:(NSManagedObjectContext*)theMOC {

    NSMutableArray *theStepsArray = [[NSMutableArray alloc] init];

    // prepare values for chart
    AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    StepSelector *theSelector = [[StepSelector alloc] init];
    NSString* apiSelectionForStep = [theSelector getCurrentSelectionString];

    for (int iter = 0; iter < steps.count; iter++) {
        NSNumber *currStepValue = [steps objectAtIndex:iter];
        // NSNumber *stepCountforIter = [NSNumber numberWithLong:[[steps objectAtIndex:iter] longValue]];

        NSNumber* dateForIter = [NSNumber numberWithLong:[[dates objectAtIndex:iter] longLongValue]];
        Step *step = [delegate addStepObjectToPersistentStorewithAPI:apiSelectionForStep
                                                         andStep:stepCountforIter
                                                         andDate:dateForIter
                                                          forMOC:theMOC];

        [theStepsArray addObject:step];

        if (VERBOSE) {
            NSLog(@"This is step number %d, with object ID: %@", count, [theMOC objectWithID:step.objectID]);
            count++;
        }
    }
    [delegate saveContext];
    return theStepsArray;
}

Thats all I can think that might help. The source for the MOC in the main target is the appDelegate which is where all the core data code was written initially.

EDIT Here is the requested PSC code. The store is located in the documents directory. I discovered that these objects are being saved to the Persistent Store.. but the error is still occurs. Se below for PSC code:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSURL *storeUrl = [self getStoreURL];

    // Rollback journalling mode...
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption,
                             NSFileProtectionComplete, NSFileProtectionKey,
                             @{@"journal_mode": @"TRUNCATE"}, NSSQLitePragmasOption, nil];

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

    NSError *error = nil;
    self.persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error];
    if (!self.persistentStore) {
        NSLog(@"Error: %@",error);
        [Error showErrorByAppendingString:NSLocalizedString(@"UnableToFindDatabaseFile", nil) withError:error];
    }

    return persistentStoreCoordinator;
}

-(NSURL *)getStoreURL {
    NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: kSQLFILENAME];
    /*
     Set up the store.
     For the sake of illustration, provide a pre-populated default store.
     */
    NSFileManager *fileManager = [NSFileManager defaultManager];
    // If the expected store doesn't exist, copy the default store.
    if (![fileManager fileExistsAtPath:storePath]) {
        NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:SQLFILEPATHRESOURCE ofType:@"sqlite"];
        if (defaultStorePath) {
            [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
        }
    }

    NSURL *storeUrl = [NSURL fileURLWithPath:storePath];

    return storeUrl;
}

Upvotes: 2

Views: 1155

Answers (1)

Tom Harrington
Tom Harrington

Reputation: 70946

The NSSQLiteErrorDomain key means that this error came from SQLite, and that Core Data is passing it back to you. SQLite defines error 1032 as follows:

The SQLITE_READONLY_DBMOVED error code is an extended error code for SQLITE_READONLY. The SQLITE_READONLY_DBMOVED error code indicates that a database cannot be modified because the database file has been moved since it was opened, and so any attempt to modify the database might result in database corruption if the processes crashes because the rollback journal would not be correctly named.

...which appears to mean that SQLite is making the persistent store file read only because something has happened to it since it was opened, and SQLite is trying to prevent data corruption.

I don't see anything in the code you've posted that is obviously at fault, at least as far as the error code description goes. So I wonder, are you doing anything anywhere else that would directly affect the persistent store file (i.e. touching the file in any way at all instead of going through Core Data fetch/save calls)?

The mention of the rollback journal in the error code description makes me wonder if setting journal_mode to TRUNCATE is related. If it were me, I'd remove that (I don't know what it's intended to accomplish here) or set it to DELETE. At least for testing purposes, anyway, in the hope of understanding the problem better.

Upvotes: 1

Related Questions