views:

27

answers:

1

I have CoreData-based data layer (using a SQLite datastore) that I am using in both an iOS app and on the server that the iOS clients talk to. The data layer (objc code + coredata model / mapping defns) is compiled into the iOS bundle as per usual, and is compiled into a Framework bundle for use on OSX.

I am hitting a brick wall with default migration using mapping models.

On iOS, it works fine. The first time running the app in the simulator after adding the new datamodel verison, it migrates all the data when you call addPersistentStoreWithType:configuration:... as per the standard Apple docs.

On OSX / PyObjC, it fails with Persistent store migration failed, missing mapping model, i.e. for some reason the mapping model .cdm file can't be found in that bundle even though it is present.

If you manually specify the source / dest / mapping models by looking them up in the bundle and then manually invoke a migration through a NSMigrationManager, everything works fine, e.g.

bundle = objc.loadBundle( "MyApp_OSX", globals(),
                          os.path.join( base, FRAMEWORK_FILENAME ) )

# URLs of input and output datastores
datastoreURL = NSURL.fileURLWithPath_( datadir + "/MyApp.hsdb" )
outURL = NSURL.fileURLWithPath_( datadir + "/MyApp-migrated.hsdb" )

# URLs of old and new version MOMs and the mapping model
momd = bundle.URLForResource_withExtension_( "MyApp.momd", None )
url1 = momd.URLByAppendingPathComponent_( "MyApp 21.mom" )
url2 = momd.URLByAppendingPathComponent_( "MyApp 22.mom" )
mappingURL = bundle.URLForResource_withExtension_( "Test.cdm", None )

# Old and new MOMs and the mapping model
mom1 = NSManagedObjectModel.alloc().initWithContentsOfURL_( url1 )
mom2 = NSManagedObjectModel.alloc().initWithContentsOfURL_( url2 )
mm = NSMappingModel.alloc().initWithContentsOfURL_( mappingURL )

# Do the migration    
migration = NSMigrationManager.alloc().initWithSourceModel_destinationModel_( 
    mom1, mom2 )

migration.migrateStoreFromURL_type_options_withMappingModel_toDestinationURL_destinationType_destinationOptions_error_(
    datastoreURL, NSSQLiteStoreType, None, mm, outURL, NSSQLiteStoreType, None, None )

At this point I have no idea why the iOS version is able to find the mapping model to successfully migrate the datastore but the OSX / PyObjC version can't, despite clearly having the mapping model in the bundle, and the mapping model apparently being valid since it works when you invoke it manually.

Any insight into how CoreData searches for valid / appropriate mapping models in bundles that might help identify how to make this work on OSX would be much appreciated.

+1  A: 

I would have suspected a permissions problem because that is one area where iOS and Cocoa differ but since the hard coded URL works, that is not the case. It looks like an NSBundle problem.

I would start by using the standard form of URLForResource:withExtension: and split up the name and extension. It' probably not the cause but the bridge is not perfect and you want to eliminate any possible sources errors.

If you've confirmed that file exist in the bundle, next check that it has a reasonable path. If you end up nesting to deep or with odd characters in path names, NSBundle may not locate the file.

TechZen
Thanks for the response TZ. I tried changing my Python to split up the stem and extension on the filename e.g. `mappingURL = bundle.URLForResource_withExtension_( "Test", "cdm" )` and it still worked in the manual method, so I don't think it's that something about filename extension detection is broken. Also, the cdm file is in the base `Resources` directory of the bundle, so I don't think there's a nesting thing going on there. There are no funny characters in the filename, so I don't think that's the culprit either. Stumped!
glenc
Also - I checked the permissions on the files in the bundle, and everything looks pretty sane. All the .cdm files are in mode 0644, as you'd expect, so I don't think it's a permissions issue.
glenc
One final thing - I don't think the PyObjC bridge should be playing a part here since the actual call to `NSPersistentStoreCoordinator#addPersistentStoreWithType:configuration:...` is done **inside** an objc method that I've implemented, and I call that method from Python rather than calling `addPersistentStoreWithType` directly. So the calling context (at least as far as function arg marshalling goes) of `addPersistentStoreWithType` should be the same in both cases ...
glenc
Just checking in if you have any more ideas on this one TZ?
glenc
I'm afraid not.
TechZen