Custom code execution after light weight migration of particular version
I have 2 object models in Core Data (say v1 and v2). This migration is eligible for light weight migration. Now, I want to execute custom code after the migration but only when the migration is from v1 to v2. Late开发者_如何学Cr on if I introduce v3, I don't want the custom code to get executed.
Is there a way to do this?
Thanks in advance, Anupam
Here is how to handle this situation, basically by using the store's metadata, as suggested by Apple engineers at WWDC 2010:
- Open the store (with migration options)
- Check metadata for custom key, e.g. “DonePostProcessing”
- Do post-processing...
- Populate derived attributes
- Insert or delete objects
- Set store metadata (“DonePostProcessing” = YES)
- Save changes and metadata
More or less, something like

- (void)loadStoreWithMigration:(NSURL *)url {
...
store = [psc addPersistentStoreWithType: NSSQLiteStoreType configuration: nil URL: url options: opts error: &err];
m = [store metadata];
key = @”DonePostProcessing”;
if (m && ([[m objectForKey: key] integerValue] < 2) ){
[self doPostProcessingInContext: context];
m2 = [[m mutableCopy] autorelease];
[m2 setObject: [NSNumber numberWithInteger: 2] forKey: key];
[store setMetadata: m2];
ok = [context save:&err];
}
}
There might be a better way, buy this should work:
Keep track of the DB version by keeping an entity in Core Data named "Information" and have a property named "CoreDataVersion".
After the migration code finishes add code to check for the version number in the core data.
If the value of "CoreDataVersion" is "v1" and your app is now at "v2" (this can be hardcoded with each version), execute the additional custom code and then write the new version back to the DB.
If you already have "v1" published to users, just say that if there is no "CoreDataVersion" in the DB, then it is "v1".
- (void) _performMaintanaceUpdate:(NSUInteger) newVersion oldVersion:(NSUInteger) oldVersion {
if (newVersion>=1020500 && oldVersion < 1020500) {
NSString *storePath = [[PreferenceDataModel getDataPath] stringByAppendingPathComponent: @"wcal.sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:storePath]) {
[fileManager removeItemAtPath:storePath error:NULL];
}
[PreferenceDataModel setVersionOfLastMaintanace:newVersion];
}
}
(void) _checkAndPerformMaintenance{
NSString* strVersion = [PreferenceDataModel getApplicationVersion];
//legal version is xx.xx.xx where x is a dec digit 1.2 or 1.2.33 is legit
NSUInteger ver = 0;
NSUInteger finalVer = 0;
int t = 3;
for (int i=0; i<[strVersion length]; i++) {
char c = [strVersion characterAtIndex:i];
if (isdigit(c)) {
ver*=10;
ver+=c - 48;
}
else {
finalVer+= ver * pow(100, t--);
ver = 0;
}
}
finalVer+= ver * pow(100, t);
[self _performMaintanaceUpdate:finalVer oldVersion:[PreferenceDataModel getVersionOfLastMaintanace]];
}
and this is how you retrieve apps version
+(NSString *) getApplicationVersion {
return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
}
Before you create the persistent store that does the automatic migration, determine if you'll be migrating to version 2. If you will be, set a flag to do your changes after the migration happens.
To determine what you'll be migrating from, do this:
NSString *path = [[NSBundle mainBundle] pathForResource:@"Model" ofType:@"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
NSString *version = [managedObjectModel.versionIdentifiers anyObject];
The version identifiers are set in Xcode.
Another way to do it is to do a custom data migration from v1 to v2. Start by looking over http://www.timisted.net/blog/archive/core-data-migration/.
Another solution that worked well for me:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger currVersion = [defaults integerForKey:@"dataModelVersion"];
NSString *version = [managedObjectModel.versionIdentifiers anyObject];
if (currVersion == 0) {
[defaults setInteger:[version integerValue] forKey:@"dataModelVersion"];
}
else if (currVersion < [version integerValue]) {
NSLog(@"Migration Code");
[defaults setInteger:[version integerValue] forKey:@"dataModelVersion"];
}
精彩评论