16

Folks,

Lightweight migration is failing for me 100% of the time on this line:

[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]

with the error:

Error: Error Domain=NSCocoaErrorDomain Code=134130 UserInfo=0x4fbff20 "Operation could not be completed. (Cocoa error 134130.)"
"Can't find model for source store";

Here is my managed object context, model, and persistent store:

- (NSManagedObjectContext *) managedObjectContext {

    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }
    return managedObjectContext;
}

- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel != nil) {
        return managedObjectModel;
    }

    managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];    
    return managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"Locations.sqlite"]];

    NSError *error;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];

    // Allow inferred migration from the original version of the application.
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
        NSLog(@"Error: %@",error);
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    

    return persistentStoreCoordinator;
}

I have two versions of my model in my project: a version 4 and a version 5. If I set my version 4 as default, it works fine. If I select "Design -> Data Model -> Add Model Version" (as described by this post), make a change, Design -> Data Model -> Set Current Version, build and run, it will fail with the aforementioned "Can't find model for source store" error. Set model back to version 4, no problems, addPersistentStoreWithType. Alternatively, if I add model version and make no changes, simply go from version 4 to 5 without adding any new fields, no problems. If I then try to go from 5 to 6, the aforementioned error.

This code is failing on both Simulator and Phone. I read several prescriptions calling for deleting and reinstalling the app, which does work for both Simulator and Phone, but I am afraid that when I release this to real users it will break my installed base since they won't be able to delete and reinstall - App Store will auto-upgrade them.

This code worked in releases past with no changes on my part - hence my ability to make it all the way up to version 4. I recently upgraded to XCode 3.2.3 building for iOS4, which may have something to do with this.

Is anyone else having this issue all of a sudden now like I am? Has anyone managed to work past it? Thanks.

PS - For Googlers who stumble on this page, here are all the relevant pages you might consider reading, below. Unfortunately none of these solved my issue.

UPDATE

While this is not a real fix, it does avoid the scenario of a crashing client: simply delete the database file:

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



    // Delete file
    if ([[NSFileManager defaultManager] fileExistsAtPath:storeUrl.path]) {
        if (![[NSFileManager defaultManager] removeItemAtPath:storeUrl.path error:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        } 
    }

    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) 
    {
        // Handle the error.
        NSLog(@"Error: %@",error);
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();

    }
}    

UPDATE 2

Here is what happens when I inspect VersionInfo.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>NSManagedObjectModel_CurrentVersionName</key>
        <string>Profile 5</string>
        <key>NSManagedObjectModel_VersionHashes</key>
        <dict>
                <key>Profile</key>
                <dict>
                        <key>Profile</key>
                        <data>
                        ZIICGgMBreuldkPXgXUhJwKamgwJzESM5FRTOUskomw=
                        </data>
                </dict>
                <key>Profile 2</key>
                <dict>
                        <key>Profile</key>
                        <data>
                        tEB7HrETWOSUuoeDonJKLXzsxixv8ALHOoASQDUIZMA=
                        </data>
                </dict>
                <key>Profile 3</key>
                <dict>
                        <key>Profile</key>
                        <data>
                        qyXOJyKkfQ8hdt9gcdFs7SxKmZ1JYrsXvKbtFQTTna8=
                        </data>
                </dict>
                <key>Profile 4</key>
                <dict>
                        <key>Profile</key>
                        <data>
                        lyWDJJ0kGcs/pUOModd3Q1ymDvdRiNXui4NCpLxDFSw=
                        </data>
                </dict>
                <key>Profile 5</key>
                <dict>
                        <key>Profile</key>
                        <data>
                        V4PyRK1ezj3xK1QFRCTVzGOqyJhEb7FRMzglrTsP0cI=
                        </data>
                </dict>
        </dict>
</dict>
</plist>

Here is the code that I wrote to inspect my model (note I had to go out and add a base64 encoder, since that is what is in the VersionInfo.plist file)

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


    NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeUrl error:&error];
    NSLog(@"%@",storeMeta);
    id someObj = [[storeMeta objectForKey:@"NSStoreModelVersionHashes"] objectForKey:@"Profile"];
    NSLog(@"%@",someObj);
    NSLog(@"%@",[NSString base64StringFromData:someObj length:[someObj length]]);

And here is the debug output:

{
    NSPersistenceFrameworkVersion = 310;
    NSStoreModelVersionHashes =     {
        Profile = <97258324 9d2419cb 3fa5438c a1d77743 5ca60ef7 5188d5ee 8b8342a4 bc43152c>;
        SerializedMessage = <4530863c d943479a edfb4dfb 5059c28d d6137dc4 d1153d36 ed52be49 11074f13>;
    };
    NSStoreModelVersionHashesVersion = 3;
    NSStoreModelVersionIdentifiers =     (
    );
    NSStoreType = SQLite;
    NSStoreUUID = "823FD306-696F-4A0F-8311-2792825DC66E";
    "_NSAutoVacuumLevel" = 2;
}

<97258324 9d2419cb 3fa5438c a1d77743 5ca60ef7 5188d5ee 8b8342a4 bc43152c>
lyWDJJ0kGcs/pUOModd3Q1ymDvdRiNXui4NCpLxDFSw=

As you can see, that last line that starts with 'ly' matches Profile 4 in VersionInfo.plist...hence I see no reason why it should be failing. Any other ideas?

Community
  • 1
  • 1
esilver
  • 27,713
  • 23
  • 122
  • 168

5 Answers5

9

I read several prescriptions calling for deleting and reinstalling the app, which does work for both Simulator and Phone, but I am afraid that when I release this to real users it will break my installed base since they won't be able to delete and reinstall.

This is a problem caused by Xcode not removing old momc files from simulator/dev-device when a the model file is changed e.g. changing the name. The old file remains which causes confusion. This is something you only see during development because it is an artifact of the way that Xcode manipulates the app bundle without completely reinstalling it every time as must happen with a release version.

You can confirm this logging the return of:

[[NSBundle mainBundle] URLsForResourcesWithExtension:@"momc"subdirectory:nil];

... which should show you all the compiled model files in the app bundle

It is bad practice to rely on migration during development because it is very common for migration to fail if you are making changes to the model and store. You should only use migration after all the code is nailed down. I would also recommend regenerating your store from scratch every time you run. It is to easy to build up garbage in the store by changing up the model.

TechZen
  • 64,370
  • 15
  • 118
  • 145
  • I was able to confirm this worked for me by using Testflightapp.com (RIP soon) to make a "release" version of my app which I was able to load onto a test device that had a previous version of the app. It installed, and ran fine, and I was able to download the database off the phone and confirm it had been migrated successfully, which was my whole point in not wanting to delete the app and reinstall through Xcode -- wanted to confirm that the migration would work, since I am using MagicalRecord 3 and had to modify some MR code since my default "stack" did not support migration OOTB. – chadbag Jan 27 '15 at 16:57
4

I've read over your updated question. It's getting quite messy with model versions.

You should try and audit the version of the model that the existing store is actually asking for, and try to list all available models in the app bundle at runtime.


I look in my apps' model directory

NSString *modelDirectoryPath = [[NSBundle mainBundle] pathForResource:@"MyModel" ofType:@"momd"];

I'm not sure if developers normally do this, but I keep my model versions with different names.. so I have in there:

  • VersionInfo.plist
  • MyModel.mom
  • MyModel2.mom

The version hashes listed in the VersionInfo.plist should help you troubleshoot. Look for the version hash required by the existing persistent store, and see if you can locate it in the version hashes listed in VersionInfo.plist.

Actually, I can't see a way to write some code that will ask the persistent store what it's entity version hashes are. The NSPersistentStoreCoordinator or the NSMigrationManger seem to be doing that privately. i.e. they check the persistent store entity versions against the model that the store is loaded with.


Had another quick look, and it's available in the store meta data. Nice and easy!

NSError *error;
NSURL *storeURL = [NSURL fileURLWithPath:[[self class] storePath]];
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeURL error:&error];

And looking in storeMeta I get a key with

<CFString 0x7328050 [0x2724380]>{contents = "NSStoreModelVersionHashes"} = <CFBasicHash 0x7328340 [0x2724380]>{type = immutable dict, count = 5,
entries =>
    0 : <CFString 0x7328110 [0x2724380]>{contents = "MyEntityNameOne"} = <CFData 0x73281b0 [0x2724380]>{length = 32, capacity = 32, bytes = 0x143325cf121239ce156af2e2a1aad7d9 ... 976977fdf29fc013}
    1 : <CFString 0x7328130 [0x2724380]>{contents = "MyEntityNameTwo"} = <CFData 0x7328200 [0x2724380]>{length = 32, capacity = 32, bytes = 0x0ca6ecf1283d12bd3ca82af39b6b9f5d ... 149dd39a591e0c4d}
    ...    }

Should be easy to iterate over that NSStoreModelVersionHashes dictionary and log the version hashes your store requires.

Manually match that back to the ones available in VersionInfo.plist and see what's missing. Perhaps there's not one single model that contains all the required versions of the entities in your existing persistent store. That might happen due to (accidental?) edits on the model before or after setting up a new model version?

ohhorob
  • 11,695
  • 7
  • 41
  • 50
  • Can you send me a link on how to list all available models in the app bundle at runtime? I don't have much luck googling on those terms. Thanks! – esilver Jun 30 '10 at 18:19
  • Thanks for these steps - I edited my original question with more information, but remain stumped. – esilver Jul 01 '10 at 14:31
  • Can you verify loading of that version of the model required? If it does load, and you can spin up an `NSPersistentStoreCoordinator` for your store using it, it might be a quirky bug with Automatic Lightweight Migration? – ohhorob Jul 02 '10 at 17:10
  • I'm opening up a bounty on this question because I have The Exact Same Problem. Want to get it sorted. – makdad Jun 23 '11 at 06:19
  • Solved my issue, added a new answer. – makdad Jun 23 '11 at 07:55
4

I had esilver's exact problem and found this question via Google. However, the fix that worked for me wasn't anywhere else on SO (that I know), so here goes:

If there are multiple copies of your *.mom files (compiled object models) in your bundle, Core Data may become confused when attempting to migrate on your behalf.

Our problem was that each individual model file (Data.xcdatamodel, Data_V1.xcdatamodel, Data_V2.xcdatamodel, etc.) was not only inside the xcdatamodeld/ directory (which was included as something to compile in the build process), but also each file was also included in the "Compile Sources" list.

What this meant is that the resulting bundle had two sets of *.mom files: one inside xcdatamodeld/ and one at the top level. I think Core Data became very, very confused, and led to this error. Removing each xcdatamodel file from the "compile sources" and leaving the xcdatamodeld directory solved the problem for us (e.g. got auto-versioning up and running again). Hope this helps!

makdad
  • 6,402
  • 3
  • 31
  • 56
  • where is this "compile sources" list? – Henley Jul 21 '11 at 00:15
  • Compile sources is in the file navigator (Xcode 3.2.x) on the left side at the bottom, inside of "targets", and on Xcode 4, click the project file name, then build phases, then compile sources. – makdad Jul 22 '11 at 07:18
0

Well in my case exactly the same thing was happening and i was on iOS 7 and this problem screwed my head for at least a week and then finally find the solution which works for me . In order to make it work you have to add an extra value in options which is used to add PersistentStore and then you go( I am not sure about other iOS version but yeah it will definitely work on iOS 7).

-(NSManagedObjectModel *)managedObjectModel
{
    if (managedObjectModel != nil)
    {
        return managedObjectModel;
    }
    managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    return managedObjectModel;
}

-(NSPersistentStoreCoordinator *)persistentStoreCoordinator
 {

   if (persistentStoreCoordinator != nil)
   {
       return persistentStoreCoordinator;
   }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"ABC.sqlite"];

    NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] ini   tWithManagedObjectModel:[self managedObjectModel]];

//Creating Lightweight migration.
    NSDictionary *options =
    @{
      NSMigratePersistentStoresAutomaticallyOption:@YES
      ,NSInferMappingModelAutomaticallyOption:@YES
      ,NSSQLitePragmasOption: @{@"journal_mode": @"DELETE"}
     };


   if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
    {
       NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
       abort();
    }
return persistentStoreCoordinator;
}
Pankaj Wadhwa
  • 3,073
  • 3
  • 28
  • 37
-2

Folks,

For the record, I ceased use of core data completely. My code was complicated and, per the issues above, unreliable. I use SQLLite directly now and am WAY happier. I cannot recommend using Core Data in any scenario.

esilver
  • 27,713
  • 23
  • 122
  • 168
  • 2
    I'm feeling pretty annoyed with Core Data too. The "magic" data migration process is ~way~ too opaque when there are problems. I'm not finding enough details in one place to remove the cover and get to the bottom of the problems that I am having with data migration. – xyzzycoder Jun 02 '11 at 04:52
  • 11
    You shouldn't mistake "I don't understand an API" for "it's to complicated". Having used both SQL and Core Data, the latter is the simpler API overall especially as the app grows more complex. I think your problems were caused by trying to migrate development builds especially trying to migrate over files that had already failed. – TechZen Jul 18 '11 at 19:26
  • 3
    In my experience, this wasn't "it's too complicated", the API flat out did not behave to spec. Core data is great for toy apps with simple migration needs; as you can see from my question and from other comments on this page, auto-migration breaks down at a certain level of complexity. Maybe they've fixed it, but I won't be going back, because SQLite both works and is easy to understand. – esilver Jul 18 '11 at 22:31
  • 1
    I tried Core Data and just gave up after a few days of total nonsensical errors when migrating. Plus, I feel you really need to write your own queries if you have an app that does a lot of synching with a web server.. – Henley Jul 22 '11 at 15:01
  • Core Data migration can be as sophisticated as you want it to be. Use a mapping model or use the migration manager. – raheel Oct 17 '12 at 05:07
  • 2
    As a heavy user of Core Data for an application that has 35 different model versions, with migrations between each, I can happily say, that this answer is 10x worse that Core Data. Yes, it may cause a few headaches, but they're there for a reason. Core Data is powerful, as long as you use it properly. Advocating that others should refuse to use it because you don't like it, or even refusing to use it simply because it's complex is not good for the public. The correct answer should be how to fix your issue. Not throw it all out entirely. – Tim Dec 04 '12 at 05:13
  • 1
    My advocacy is based on my experience of an intractable bug, explicitly detailed on this page. The fact that this page has been viewed over 3,000 times suggests I am not the only one who is encountering these problems. Consider yourself fortunate that you have not hit them as well. – esilver Dec 04 '12 at 17:16
  • 2
    Update: apparently the dev community shares my experience with this piece of technology: https://news.ycombinator.com/item?id=5454491 – esilver Mar 28 '13 at 16:36
  • 2
    That's iCloud, not core data. I agree, core data sync with iCloud is horribly broken. But core data itself is amazing. – Jason May 24 '13 at 02:12
  • How can you say "I cannot recommend using Core Data in any scenario" when lots of apps use it successfully? This is the usual frustrated feedback we get from people that don't understand Core Data. I had great success using Core Data from small to large applications. I suggest people learn Core Data before giving these kinds of subjective comments. – zumzum Jun 23 '14 at 17:38
  • Granted this was written years ago, but read the hell (documented above) that Core Data put me through. This was not me as a novice writing hello world - this was a very serious and technical issue. If you are signing up for Core Data, you were signing up to debug intractable problems like this one. I hope Apple has improved things since. – esilver Jun 24 '14 at 02:56