Cannot find a mapping model for migration - UIManagedDocument master data migration

I have two versions of my model Model001.xcdatamodel and Model002.xcdatamodel . The two are in the Model.xcdatamodeld package. I also have Model001to002.xcmappingmodel , which is not part of Model.xcdatamodeld . I checked: both xcmappingmodel and xcdatamodeld will be copied to the .app package.

My managed object context is initialized as follows:

  NSURL *documentModel = [bundle URLForResource:@"Model" withExtension:@"momd"]; managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:documentModel]; return managedObjectModel; 

I also set these properties in my overridden initWithFileURL: in my subclass of UIManagedObject .

  NSMutableDictionary *options = [NSMutableDictionary dictionaryWithDictionary:self.persistentStoreOptions]; [options setObject:@YES forKey:NSMigratePersistentStoresAutomaticallyOption]; [options setObject:@YES forKey:NSInferMappingModelAutomaticallyOption]; self.persistentStoreOptions = [options copy]; 

But when I try to open documet, I get the following error: Can't find mapping model for migration

- UPDATE -

Even if I do manual migration

  [NSMappingModel mappingModelFromBundles:@[[NSBundle mainBundle]] forSourceModel:sourceObjectModel destinationModel:self.managedObjectModel]; 

this returns zero. Although I double checked that Model001to002.cdm is in the application bundle. It must be in the application bundle correctly?

+6
source share
5 answers

OK, I solved the problem by deleting all the master data files from Xcode, reading them and again setting the source and destination of the mapping model.

Damn you Xcode!

+2
source

The "gotcha" with matching models is that you are not allowed to make any changes to the models after creating the matching. If you do this, you will also get this error.

+19
source

You are not allowed to make changes to the source / target model after creating the matching models.

If you make some changes,

  • mappingModelFromBundles:forSourceModel:destinationModel: cannot find a mapping model file
  • addPersistentStoreWithType:configuration:URL:options:error: with {NSInferMappingModelAutomaticallyOption: @NO} will report an error "Cannot find a mapping model for migration"
  • migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error: will report an error "Mismatch between matching and source / target models"

So, just recreate the display model and copy all the changes you made in the old version.

+3
source

This can happen if the test device storage is from a version of the data model that no longer exists.

For example, I had a version 7 data model, then I made a Data Model Version 8. I made a mapping model to go from 7 to 8. Then I ran it on my test device and everything was happy.

Then I made a few more changes in 8.

It should be understood that in Core Data each model has a hash identifier created by the system, taking the checksum of the xcdatamodel file. Therefore, if you make even a small change, even if you have not created a new version, it treats it as a different version. Identifiers for these versions of NSStoreModelVersionHashes (see the documentation here ).

In other words, I ended up with:

 Data Model 7 (release) - 0plcXXRN7XHKl5CcF+fwriFmUpON3ZtcI/AfK748aWc= Data Model 8 (beta) - qeN1Ym3TkWN1G6dU9RfX6Kd2ccEvcDVWHpd3LpLgboI= Data Model 8 (release) - EqtMzvRnVZWkXwBHu4VeVGy8UyoOe+bi67KC79kphlQ= 

Instead of making version 9 and keeping the original version 8 in the history of the data model, I just updated 8, because automatic migration could take care of me. Well, this could not, and I could not make a comparison between the two, because the old (beta) version 8 disappeared.

I did it this way because it was an intermediate internal build (not a release), so it didn't matter, but it got me in a loop!

If it was not an internal assembly, and I needed to do this work, I could go back to commit (beta) and pull out this xcdatamodel file for 8 (beta), rename the version (release) to 9, then paste it into the assembly release and create a mapping model between 8 and 9.

However, since it was just a built-in beta, we just wiped and reinstalled the application on test devices. We made sure that during the transition from 7 (release) to 8 (release), migration went smoothly.

0
source

TL DR

At least with Xcode 8/9, open the mapping model, then select Update Data Models from the Editor menu. Usually you need to restart Xcode. If this is not the case, you can try reinstalling the destination at the bottom of the model editor.

Additional tips

Definitely NEVER change the model after it is distributed in the application assembly.

In this example, suppose you publish Data Model 1 (DM1) and move on to DM2. If you install DM2 as the active version, then run the application, the migration will be launched in your permanent storage. If you then make another change to DM2, launch the application ... Boom!

The problem is that your store has already been transferred to "DM2", but the data in the store no longer fits into the model. And we cannot switch from DM2 to DM2 again.

This may seem like an obvious solution and create a DM3. this is usually a good idea, although minimizing the number of models and migrations during development.

So ... now you have a permanent store that has been migrated to a non-existent DM2. How do you test migration again? You can return your application and generate some data using DM1, but I prefer to use backups

Backup

Before starting the application with DM2, you can copy the existing storage (with DM1), which will be used for subsequent test migrations. On macOS, you can easily do this manually. The code below should also do the trick. Normally, you would not want to send this, rather, you could just put it somewhere before your normal CD stack opens, start the application, and then stop the application (maybe put a breakpoint right after you finish working through Xcode).

 let fm = FileManager.default let url = // The store URL you would use in ↓ // try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil) let dir = url.deleteLastPathComponent().appendingPathComponent("Backup", isDirectory: true).appendingPathComponent("DM1", isDirectory: true) print("Saving DB backup for DM1") if !fm.fileExists(atPath: dir.path) { do { // Create a directory try fm.createDirectory(at: dir, withIntermediateDirectories: true, attributes: nil) let backupURL = dir.appendingPathComponent(url.lastPathComponent) try fm.copyItem(at: url, to: backupURL) } catch { print("Failed to save DB backup") } } 

Sorry, I need to make another change ...

If you start your migration to DM2, then realize that you need to make another change, you will want to double-check your migration from DM1 β†’ DM2. This is where the backup takes place.

Just as you made a backup, run this code.

 let fm = FileManager.default let url = // The store URL you would use to add the store let dir = url.deleteLastPathComponent().appendingPathComponent("Backup", isDirectory: true).appendingPathComponent("DM1", isDirectory: true) let backupURL = dir.appendingPathComponent(url.lastPathComponent) if fm.fileExists(atPath: backupURL.path) { do { fm.removeItem(at: url.path) try fm.copyItem(at: backupURL, to: url) } catch { print("Failed to restore DB backup") } } 

You now have a refurbished DM1 store and changes to DM2. If you run the application, the migration may succeed, but it will not use your custom mapping model.

Remember that if you use custom mapping, you still have to use the Update Data Models technique before the mapping model works.

0
source

Source: https://habr.com/ru/post/927186/


All Articles