Last active
December 15, 2015 06:28
-
-
Save irace/5216137 to your computer and use it in GitHub Desktop.
Core Data migrations are hard. If you don't need to migrate, then don't! Here's how:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Core Data is great but automatic migrations can be tricky. Migrations can take a long time, which could | |
result in [your app being terminated](http://stackoverflow.com/questions/13333289/core-data-timeout-adding-persistent-store-on-application-launch) | |
if it is happening on the main thread during application launch. Performing migrations on a background | |
thread is also a [bad idea](http://stackoverflow.com/a/2866725/503916), meaning your application really | |
needs to be able to fully launch *without a Core Data stack whatsoever* in order to safely migrate. | |
This can be a huge change to make to an existing app. | |
If you're really only using Core Data as a cache, you don't actually *need* to perform a migration. | |
Simply check if the existing store is compatible with your managed object model and if so, delete | |
and recreate it. Here's my approach for doing so. Many thanks to Marcus Zarra for being *extremely* | |
helpful both over Twitter and on Stack Overflow. | |
*/ | |
NSString *persistentStorePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, | |
YES) lastObject] stringByAppendingPathComponent:@"AppName.sqlite"]; | |
// Create managed object model | |
NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL: | |
[[NSBundle mainBundle] URLForResource:@"AppName" | |
withExtension:@"momd"]]; | |
// Create persistent store coordinator | |
NSPersistentStoreCoordinator *persistentStoreCoordinator = | |
[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel]; | |
// Create managed object context | |
managedObjectContext = [[NSManagedObjectContext alloc] init]; | |
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; | |
managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator; | |
NSURL *persistentStoreURL = [NSURL fileURLWithPath:persistentStorePath]; | |
if ([[NSFileManager defaultManager] fileExistsAtPath:persistentStorePath]) { | |
// If store already exists, see if it is compatible with managed object model | |
NSError *storeMetadataError = nil; | |
NSDictionary *storeMetadata = | |
[NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType | |
URL:persistentStoreURL | |
error:&storeMetadataError]; | |
if (storeMetadataError || ![managedObjectModel isConfiguration:nil | |
compatibleWithStoreMetadata:storeMetadata]) { | |
// Store is not compatible - wipe it | |
NSError *removeStoreError = nil; | |
[[NSFileManager defaultManager] removeItemAtPath:persistentStorePath error:&removeStoreError]; | |
if (removeStoreError) | |
NSLog(@"Error removing file at path '%@': %@, %@", path, removeStoreError, | |
[removeStoreError userInfo]); | |
} | |
} | |
NSError *addStoreError = nil; | |
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil | |
URL:persistentStoreURL options:nil | |
error:&addStoreError]) | |
NSLog(@"Unable to add store: %@, %@", addStoreError, [addStoreError userInfo]); |
The code looks good to me. Removing the instance level ivars removes the risk of someone overriding an accessor and doing something silly.
Thanks!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
My original problem was a Core Data migration that was blocking the main thread for too long during startup. Since I'm really only using Core Data as a cache, I don't actually need to perform any migrations. This is the approach that I came up with instead, which I owe largely to your help on both Twitter and Stack Overflow.
I updated this to just use local variables instead of instance variables. This example isn't necessarily supposed to suggest where this code should live, more so just to serve as an example of the steps to take.