Enable Automatic Lightweight Migration of Core Data on NSPersistentDocument
Asked Answered
B

2

5

ALM is great. But I can't get it to work on a project that uses Core Data with NSDocument. It seems that ALM is disabled by default.

Fine. For any normal project, you'd add the two appropriate options to the dictionary in this line:

if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error])

...but that line doesn't exist when using CD with NSD (NSPersistentDocument hides the details and I can't see how you can modify them).

The following method seems to offer hope:

configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error:

...some people on mailing lists (from 4+ years ago) report success overriding that method, altering the options dictionary, and then re-invoking it on "super". That didn't work for me, sadly. I would advise trying that first if you have similar problems - then come back here if it still doesn't work :)

Bituminize answered 16/5, 2011 at 0:14 Comment(0)
B
8

Making a new Cored-Data-with-NSDocument project from scratch, I tried the approach that didn't work originally, and it worked fine this time:

-(BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)url ofType:(NSString *)fileType modelConfiguration:(NSString *)configuration storeOptions:(NSDictionary *)storeOptions error:(NSError **)error
{
    NSMutableDictionary *newOptions = [NSMutableDictionary dictionaryWithDictionary:storeOptions];
    [newOptions setValue:@"YES" forKey:NSMigratePersistentStoresAutomaticallyOption];
    [newOptions setValue:@"TRUE" forKey:NSInferMappingModelAutomaticallyOption];

    return [super configurePersistentStoreCoordinatorForURL:url ofType:fileType modelConfiguration:configuration storeOptions:newOptions error:error];
}
Bituminize answered 6/6, 2011 at 21:2 Comment(0)
B
1

NB: I suspect the root cause of my problems is that Xcode4 is incorrectly updating the private hashes it uses instead of version numbers to track a data-model version. Probably I accidentally added something, then deleted it, and it changed the hash - my original model was so very simple it was easy to compare by eye, and there were no differences.

Also, more generally, the problem is that Xcode4 still doesn't handle CoreData projects properly - it creates CoreData models as "versioned" by default (big improvement from Xcode3, which was doomed to always create useless models), but it still doesn't do any handling of changes in the model - you must manually remember to update the version BEFORE you save any changes (or else all your project migration will fail, forever, with no way out).

Also, I couldn't find any "correct" solution once things go wrong - just too many missing pieces from Apple's Core Data lib. Basically: NSPersistentDocument is incomplete, unsupported, so far as I can tell.


In the end, I went with the following BRUTAL workaround: instructed everyone with old versions to manually edit the CoreData stores to claim they are running the current version.

This works 100%, because unlike CoreData, my app is written to intelligently correct any obvious missing data on import. It's trivial, but CoreData doesn't allow you to say "yes, I've done this. It's OK. Go ahead and open the file!"

  1. in old version: "save as XML"
  2. gave them the new header to copy/paste into top of file. NB: Apple uses hashes instead of version numbers, which makes this look scary, even though it isn't. I don't understand why Apple is ignoring their own version system and using hashes instead?
  3. in new version: open the updated file
  4. in new version: "save"

...and because I'm overriding the NSManagedObject method:

-awakeFromInsert

and fixing any incorrect/missing data, the steps 3 and 4 above "automagically" update the data.

I know that the "correct" way would have been to create a mapping model, but it's massive overkill for most situations ... and Apple was refusing to load the old data in the first place. So, Core Data was just refusing to even allow me to correct the data - even though my code was happily doing so.

(all old projects have imported and exported correctly - no problems)

IMHO: CoreData really needs some re-design on Apple's end to fix all the edge-cases they didn't think about first time around.


NB: quick warning: make sure you DON'T change any CoreData variables inside awakeFromFetch - Apple temporarily disables change-tracking before calling that method (which is kind of laughable - it makes the method incompatible with the behaviour of all other "awakeFrom*" methods).

Apple's advice: inside awakeFromFetch, decide what you're going to change, then package it up and use something like this:

[self performSelector:@selector(myAwakeFromFetchFixItemX:) withObject:X afterDelay:0.01];

...just thought I'd add that for anyone trying this - otherwise your import will work fine, but your export will silently fail to include the fixed data!

Bituminize answered 30/5, 2011 at 14:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.