收录日期:2020/10/28 10:24:42 时间:2011-03-04 16:45:25 标签:core-data,migration,bundle,pyobjc,mapping-model

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.

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.