BlackDog Foundry Bookmark This page

I recently made some core data modifications to LogDiver – my excellent log parsing tool and planned to use Automatic Migration to migrate existing databases.

!!! STOP THE PRESS !!!

While the techniques described in this post are technically correct and result in a working application, it won’t pass Mac App Store submission with those temporary entitlements. Fear not, though – I opened a DTS incident and now have another post that contains an Apple-approved mechanism.

I will leave this post here for historical purposes though.

The Problem

However, what I found was that every time I attempted to open a file with the old data model (thus triggering automatic migration), I would get the following error:

Core Data Migration Error

And my console reported:

encountered the following error: {
    NSLocalizedDescription = "The document \U201cold_model.logdive\U201d could not be opened. An error occured during persistent store migration.";
    NSUnderlyingError = "Error Domain=NSCocoaErrorDomain Code=134110 \"An error occured during persistent store migration.\" 
    UserInfo=0x1002d8b30 {sourceURL=file://localhost/Users/edwardsc/source/tmp/old_model.logdive, 
    reason=Can't copy source store to destination store path, destinationURL=file://localhost/Users/edwardsc/source/tmp/.old_model.logdive.migrationdestination_41b5a6b5c6e848c462a8480cd24caef3, 
    NSUnderlyingError=0x1002daec0 \"The operation couldn\U2019t be completed. (NSSQLiteErrorDomain error 14.)\"}";
    destinationURL = "file://localhost/Users/edwardsc/source/tmp/.old_model.logdive.migrationdestination_41b5a6b5c6e848c462a8480cd24caef3";
    reason = "Can't copy source store to destination store path";
    sourceURL = "file://localhost/Users/edwardsc/source/tmp/old_model.logdive";
}

This isn’t particularly helpful. After many hours of googling for NSSQLiteErrorDomain error 14 with no luck, I gave up and started to do some more research.

Errors not being populated

So, upon initial investigation, it looks like it can’t copy the file to a temporary file for migration. I checked and re-checked my file and directory permissions and everything looked OK.

I set a breakpoint in my implementation of configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error: which, BTW, looks something like:

-(BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)url ofType:(NSString *)fileType modelConfiguration:(NSString *)configuration storeOptions:(NSDictionary *)storeOptions error:(NSError **)error {
	NSMutableDictionary *options = (storeOptions != nil) ? [storeOptions mutableCopy] : [[NSMutableDictionary alloc] init];
 
	options[NSMigratePersistentStoresAutomaticallyOption] = @(YES);
	options[NSInferMappingModelAutomaticallyOption] = @(YES);
 
	BOOL ok = [super configurePersistentStoreCoordinatorForURL:url ofType:fileType modelConfiguration:configuration storeOptions:options error:error];
	NSLog(@"OK=%@", @(ok));
	return ok;
}

and even though the call to super was returning NO, the error variable didn’t seem to being populated. Weird! Anyway, if I let the program keep running, eventually somewhere in the Apple framework it dumps out the error message reported above.

Core Data debug info

So, I added the following flag to my application’s runtime arguments:

-com.apple.CoreData.SQLDebug 3

and now, I get the following additional output:

CoreData: annotation: Connecting to sqlite database file at "/Users/edwardsc/source/tmp/old_model.logdive"
CoreData: sql: pragma cache_size=500
CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA
CoreData: annotation: Disconnecting from sqlite database.
Failed to delete support directory for store: /Users/edwardsc/source/tmp/.old_model.logdive.migrationdestination_41b5a6b5c6e848c462a8480cd24caef3

Aha… so, I can’t delete the support directory (whatever that means)! Again, after many more hours of unsuccessfully googling for that phrase, I found another Core Data flag.

Automatic migration debug info

This time I added the following flag to the application’s runtime:

-com.apple.CoreData.MigrationDebug 1

and I get some more info:

CoreData: annotation: (migration) will attempt automatic schema migration
CoreData: annotation: Disconnecting from sqlite database.
CoreData: annotation: (migration) looking for mapping model with source hashes: 
CoreData: annotation: (migration) no suitable mapping model found
CoreData: annotation: (migration) inferring a mapping model between data models with source hashes: 
Failed to delete support directory for store: /Users/edwardsc/source/tmp/.old_model.logdive.migrationdestination_41b5a6b5c6e848c462a8480cd24caef3
CoreData: annotation: (migration) failed to copy store file from file://localhost/Users/edwardsc/source/tmp/old_model.logdive to file://localhost/Users/edwardsc/source/tmp/.old_model.logdive.migrationdestination_41b5a6b5c6e848c462a8480cd24caef3. (Error Domain=NSSQLiteErrorDomain Code=14 "The operation couldn’t be completed. (NSSQLiteErrorDomain error 14.)" UserInfo=0x100238310 {NSFilePath=/Users/edwardsc/source/tmp/.old_model.logdive.migrationdestination_41b5a6b5c6e848c462a8480cd24caef3, reason=Failed to open destination database})
CoreData: annotation: (migration) leaving incompletely migrated store on disk after automatic migration failure. (file://localhost/Users/edwardsc/source/tmp/.old_model.logdive.migrationdestination_41b5a6b5c6e848c462a8480cd24caef3)
CoreData: error: (migration) migration failed with error Error Domain=NSCocoaErrorDomain Code=134110 "An error occured during persistent store migration." UserInfo=0x101a48630 {sourceURL=file://localhost/Users/edwardsc/source/tmp/old_model.logdive, reason=Can't copy source store to destination store path, destinationURL=file://localhost/Users/edwardsc/source/tmp/.old_model.logdive.migrationdestination_41b5a6b5c6e848c462a8480cd24caef3, NSUnderlyingError=0x100254cb0 "The operation couldn’t be completed. (NSSQLiteErrorDomain error 14.)"}

Hmmm – so, we’ve got more info in there, but still totally useless in terms of telling me what is wrong.

Some more searching goes on, and eventually I find a couple of posts that give me a glimmer of an idea.

The Solution

It turns out that automatic migration needs to copy the core data database to a temporary file so that it can perform the migration. This seems sensible, until it has to run in a Sandboxed environment where the user hasn’t given explicit permission to write to that temporary file.

It wouldn’t be so bad if it had at least output a meaningful error message instead of the nonsense above.

Anyhoo, the way to get around it is to create a couple of temporary entitlements – namely:

<key>com.apple.security.temporary-exception.files.absolute-path.read-write</key>
<string>/Volumes/</string>
<key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key>
<string>/</string>

Once you have these added to your entitlements file automatic migration works as advertised. Hopefully, this will save someone else the pain I just went through.

2 Comments »

  1. zs says:

    Running into the same issue, but on iOS10 with Xcode8. Can’t use temporary entitlements there unfortunately. Any other work-arounds that you can suggest?

  2. craig says:

    At the top of this article, I mention that temporary entitlements are not really a workable solution and link to another post where I describe a better answer – http://www.blackdogfoundry.com/blog/light-weight-core-data-migration-in-a-sandboxed-app

Leave a Comment »




Categories

Copyright © 2012 BlackDog Foundry