Working on the book, I discovered that there is a real gotcha in using migrations with Core Data on the iPhone. So far as I can tell, the way that you need to change your project for migrations isn't documented anywhere. It may be in there somewhere, but it certainly doesn't jump out at you.
After you create the first new version of your data model, the first thing you have to do (if using automatic migrations) is enable the automatic migrations in your persistent store coordinator. This step actually is documented, and it involves just creating an NSDictionary and passing it in when you create your persistent store coordinator. The modifications to the accessor method that was created for you automatically in your application delegate are in bold below:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
From reading the documentation, it looks like that's all you have to do. Since you're done, you dutifully fire up your application and get…
Can't merge models with two different entities named 'Foo'Well, Frack.
Here's what's going on. The .xcdatamodel classes don't get copied into your application bundle as-is. Instead, each one gets compiled into a new file of type .mom (managed object model). When you add a new version to your project, the current version gets compiled into a .mom file in your application's bundle in the Resources folder. It doesn't stop there, thought. It also creates a folder (technically, a bundle) in Resources named after your data model but with a .momd extension. Inside the .momd bundle, it puts a compiled .mom file for each of the other versions of your data model. This is understandable. It needs this information to do the automatic migration.
Here's the problem, though. The default managedObjectModel accessor method that gets created on your application delegate creates a managed object model using a factory method called mergedModelFromBundles:. This method iterates through all the files in your application's bundle looking for all .mom. It iterates the directory structure, even going down into folders and bundles. Here's what the method looks like as created for you:
- (NSManagedObjectModel *)managedObjectModel
The managedObjectModel hmethod is completely unaware of the fact that a .momd folder contains older versions of the same data model, and so it attempts to merge every version of your data model into a single merged model. Since different versions of the same data model typically have the same entities, the merge fails and you get an error message similar to the one above because entities must be unique. You can't have too "foo" entities in a single data model.
Here's the step that seems to have been missed in the documentation1. If you want to use migrations, you have to manually create your data model from the .momd bundles. Yep, can create a data model by providing a path to the .momd bundle instead of to a .mom file, and that's what you should be doing if you're going to use versioning. Here's what the new version might look like if you've got a single data model:
- (NSManagedObjectModel *)managedObjectModel
NSManagedObjectModel requires a URL to manually specify the location of a file, so we first get the path from NSBundle, then use it to create a file URL, then use that to load the data model file.
With this new version in place, automatic migrations should work, and you should be set up to do manual migrations based on the instructions in Apple's documentation.
1 - In Apple's defense, the documentation does say you can do this, it just doesn't make it clear that you need to.
No comments:
Post a Comment