No se puede encontrar el model de mapeo para la migration: migration de datos básicos de UIManagedDocument

Tengo dos versiones de mi model Model001.xcdatamodel y Model002.xcdatamodel . Estos dos están en el package Model.xcdatamodeld . También tengo un Model001to002.xcmappingmodel que no es parte de Model.xcdatamodeld . Comprobé: tanto xcmappingmodel como xcdatamodeld se copyn en el package .app.

Mi context de object gestionado se inicializa así:

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

También configuro estas properties en mi initWithFileURL: anulado initWithFileURL: en mi subclass UIManagedObject .

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

Pero cuando bash abrir un documet, aparece el siguiente error: Can't find mapping model for migration

– ACTUALIZACIÓN –

Incluso si hago una migration manual

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

esto devuelve cero. Aunque comprobé dos veces que el Model001to002.cdm está en el set de aplicaciones. Tiene que estar en el package de aplicaciones ¿verdad?

Un "gotcha" con models de mapeo es que no se le permite hacer ningún cambio a los models después de crear el mapeo. Si lo hace, también recibirá este error.

No tiene permitido realizar ningún cambio en el model de origen / destino después de haber creado los models de mapeo.

Si realiza algunos cambios,

  • mappingModelFromBundles:forSourceModel:destinationModel: no podrá encontrar el file del model de mapeo
  • addPersistentStoreWithType:configuration:URL:options:error: con {NSInferMappingModelAutomaticallyOption: @NO} se informará un error "No se puede encontrar el model de asignación para la migration"
  • migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error: informará un error "No coincide entre la asignación y los models de origen / destino"

Entonces, simplemente recree el model de mapeo y copie todos los cambios que realizó en el anterior.

OK, resolvió el problema eliminando todos los files de datos centrales de Xcode, leyéndolos y configurando de nuevo la fuente y el destino del model de asignación.

¡Maldición, Xcode!

Esto puede suceder si la tienda de su dispositivo de testing es de una versión del model de datos que ya no existe.

Por ejemplo, tenía la versión de model de datos 7, luego hice la versión de model de datos 8. Hice un model de mapeo para pasar de 7 a 8. Luego lo ejecuté en mi dispositivo de testing y todo estaba contento.

Luego hice algunos más cambios a 8.

Lo que hay que darse count es que en Core Data, cada model tiene un identificador hash que el sistema crea tomando una sum de comprobación del file xcdatamodel. Entonces, si realiza incluso un pequeño cambio, incluso si no creó una nueva versión, la verá como una versión diferente . Los identificadores de estas versiones son NSStoreModelVersionHashes (ver documentation aquí ).

Entonces, en otras palabras, terminé con:

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

En lugar de hacer una versión 9 y save la versión original 8 en el historial del model de datos, acabo de actualizar 8, calculando que la migration automática podría cuidarme. Bueno, no podía, y no pude hacer un mapeo entre los dos, porque la vieja versión (beta) de 8 ya no estaba.

Lo hice de esa manera porque era una compilation interna intermediaria (no una versión), así que no fue un gran problema, pero ¡me tiró por un bucle!

Si no fuera una compilation interna y tuviera que hacer que esto funcione, podría volver a la confirmación (beta) y extraer ese file xcdatamodel para 8 (beta), cambiar el nombre de la versión (versión) a 9 y luego pegarlo en la versión de compilation y realice un model de mapeo entre 8 y 9.

Sin embargo, dado que era solo una versión beta interna, simplemente eliminamos y reinstalamos la aplicación en dispositivos de testing. Verificamos que, al pasar de 7 (lanzamiento) a 8 (lanzamiento), la migration fue sin problemas.

TL; DR

Al less a partir de Xcode 8/9, abra el model de mapeo, luego en el menu Editor select Actualizar models de datos . Por lo general, parece que necesitas reiniciar Xcode. Si no lo hace, intente volver a seleccionar el destino en la parte inferior del editor de models.

Mas consejos

Definitivamente NUNCA cambie un model después de que se haya distribuido en una compilation de aplicaciones.

Para este ejemplo, digamos que ha publicado el Modelo de datos 1 (DM1) y está realizando una migration a DM2. Si configura DM2 como la versión activa y luego ejecuta su aplicación, se ejecutará una migration en su tienda persistente. Si luego realiza otro cambio a DM2, ejecute su aplicación … Boom!

El problema es que su tienda ya se ha migrado a "DM2", pero los datos en la tienda ya no encajan en el model. Y, no podemos migrar de DM2 a DM2 nuevamente.

Puede parecer una solución obvia para seguir adelante y crear DM3. Por lo general, es una buena idea minimizar la cantidad de models y migraciones mientras se está desarrollando.

Entonces … ahora tienes una tienda persistente que se ha migrado a un DM2 extinto. ¿Cómo testings de nuevo la migration? Podría revertir su aplicación y generar algunos datos con DM1, pero prefiero usar copys de security

Crear una copy de security

Antes de ejecutar su aplicación con DM2, puede copyr la tienda existente (con DM1) para utilizarla para las migraciones de testing posteriores. En MacOS puede hacerlo fácilmente de forma manual. El código de abajo debe hacer el truco también. Normalmente no querrías enviar esto, sino que podrías ponerlo en algún lugar antes de que se abra tu stack de CD normal, ejecute la aplicación y luego detenga la aplicación (tal vez coloque un punto de interrupción justo después de finalizar la ejecución a través de 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") } } 

Vaya, tengo que hacer otro cambio …

Si ejecuta su migration a DM2 y luego se da count de que necesita hacer otro cambio, deseará volver a probar su migration desde DM1 -> DM2. Aquí es donde entra la copy de security.

De la misma manera que hizo la copy de security, ejecute este código.

 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 restre DB backup") } } 

Ahora tiene una tienda DM1 restaurada y ha realizado cambios en DM2. Si ejecuta la aplicación, la migration podría tener éxito, pero no utilizará su model de asignación personalizado.

Recuerde que si está utilizando un mapeo personalizado, necesitará utilizar la técnica Actualizar models de datos antes de que el model de mapeo funcione.