I actually have a project with 6 core data models like V1 to V6,all are on app stores now when I am trying to update app from one of the older version (core data modelV3) to the current version it crashes (schema mismatch)
when investigated actually V3 model version is different between two project version from older V3 Model to current Model V3.
as in V3 version i added new entity and did not do LightWeight migration(forgot to do it), now is it any way I can allow users to upgrade this older app version with V3 model to the current app version like by solving this (Missing migration issue)
Please suggest any way to add a missing migration model if possible to provide smith upgradtion of the older app version to the current app version.
addPersistentStore should automatically perform necessary lightweight migration. Check if that code is reachable in all your application starting paths.
For that to happen, you need to supply correct set of options, as below:
do {
let options = [ NSInferMappingModelAutomaticallyOption : true,
NSMigratePersistentStoresAutomaticallyOption : true]
try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType,
configurationName: nil,
at: persistentStoreURL,
options: options)
} catch {
fatalError("Problem loading Persistent Store")
}
Please also make sure your main xcdatamodel file points to latest version of your datamodel. Also note that following picture is taken after selecting your latest version xcdatamodel file (yourdatamodel 2.xcdatamodel), not the root xcdatamodel file (yourdatamodel.xcdatamodel).
A word of caution:
Reconsider: do you really need something beyond lightweight? In most situations, lightweight should resolve your problems.
You can get away with lightweight by adding more fields and stop using older fields in your code, thus making the task easier for Core Data. For rows relying on older fields, simply run some insert/update/delete operation if core data model version change is detected.
In order to check where lightweight fails, you can consider enabling following XCode argument:
-com.apple.CoreData.MigrationDebug 1
If lightweight isn't capable of fully migrating your model (such as you have renamed your entities, or data types have changed, which is very unrealistic and you should really reconsider your design if you are doing this), then you can consider this somewhat complex solution:
add persistent store with older model version and back up existing rows in some external file (non core data)
deleting the old persistent store and recreate it using addPersistentStore + newer model version
use newer persistent store to insert backed up data
If all else fails, then consider mapping model alternative. Refer to this article for more details.
Related
I have a model Version. The entity passPhrase of Version needs to be migrated to New entity passPhrase in Home model.
But the Home model is introduced in new version of database. It was not present in old xcdatamodel. How can I migrate data from
Version.passPhrase to Home.passPhrase
And the golden rule of CoreData migration is - avoid custom migration at any cost. :)
So the way to do this to use lightweight migration to add your new entity, and then just use a one off migration script to move your data over.
You can remove the passPhrase property from your Version class definition but you need to leave it in the model for now so you can still access the old data. This can be cleaned up at some time in the future when you are confident all of your users have upgraded to the newer version, or just leave it there forever.
To access the existing value during your copy just use [version valueForKey:#"passPhrase"] and then once copied clear it out the same way [version setValue:nil forKey:#"passPhrase"]
In core data, Changing Entity / Model mapping does not comes under light weight migration. You have to handle it manually by subclassing NSMigrationManager and implementing custom NSEntityMigrationPolicy.
Apple provides little documentation on the subject.Please check the Custom Core data migration with detailed example. Hope this will help.
I was referring a wonderful tutorial Swift Core Data Tutorial for migrations in core data.
The tut stated that migrations in core data go hand in hand with multiple versions of data model.
I was experimenting on light-weight migrations and got to figure out the following points:
Scenario - I had a data model and I tried to add a some new attributes to an entity and I got an error as -
iOS Version - 9.1
Swift Version - 2.2
"The model used to open the store is incompatible with the one used to
create the storeā
I then tried to enable migration without creating any new version by adding the code -
let options = [ NSMigratePersistentStoresAutomaticallyOption : true, NSInferMappingModelAutomaticallyOption : true ]
try persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: URLPersistentStore, options: options)
Then I tried altering the database similarly, i.e added an attribute to an entity and hurrah !!, it didn't result in a crash. Even I tried to access the particular entity as well as attribute but did it successfully.
What happened is migration without having multiple versions.
Question -
So Is migration possible without having multiple versions of data model?
Link to sample code - https://www.dropbox.com/s/mnb2dzxn56ghuet/CoreDataSwift-NSFetchedResultsController-master-2.zip?dl=0
Beginning with iOS 9, Core Data will copy the data model to the persistent store, and use that as the starting point if lightweight migration is necessary and requested. As a result, lightweight migration is possible even if the old model version is not available. This only works with SQLite persistent stores, and only for lightweight model migrations.
This was described in the What's New in Core Data session at WWDC 2015.
So I'm working on a project which has been published to users. Before I put my hand on this project, this project has an 1.0 and an 2.0 version, v2.0 modified the data model of core data, but the one who did this didn't add a new version to data model.
So when user upgrade from v1.0 to v2.0, it will crash.
My problem is now I'm deploying v3.0, how do I modify my xcdatamodel so that user with v1.0 and v2.0 can both upgrade to v3.0 without problem?
Get the V1 model and add a new version to it that is identical to v2 and then add another V3 model to that so that you end up with a model that has all three versions. That way you should be able to use lightweight migration from either of the previous versions, assuming that your model changes will in fact support lightweight migration. If not then do a manual migration as suggested by #Mundi. You can recover the V1 model from the V1 app bundle if you no longer have the source code but you do need a copy of the V1 app bundle to do so. You might have to google for how to do this or let me know and I will hunt around as I will have it documented somewhere... Hope this helps.
The only feasible solution that comes to mind seems to be to include two data models in the bundle. (Essentially, the versioning system does something similar.)
Read the data with the first model, then "manually" copy it to the data store of the second model (modifying it as necessary). From then on, only use the second model (which should be versioned for future changes).
I would block the UI and put up a message like "migrating your data" to make sure no user interaction interferes with this process.
In our existing app, we have many different version of xcdatamodel:
+ TheApp.xcdatamodel
TheApp.1.0.xcdatamodel
TheApp.1.1.xcdatamodel
TheApp.2.0.xcdatamodel
...
We know that the traditional way of doing database migration testing is what have been proposed in this question: How to Test Core Data Migration With an App Already in the App Store? In short, it works like the following:
install a old version of the app;
create some data in the old version of the app;
install the new version on top it;
see if everything is migrated properly.
We have been using this migration testing method for all our previous version of the app. Our QAs will perform the above steps and then judge by themselves that whether migration is successful or not.
However, in the most recent upgrade, we have changed a lot in our data model. It doesn't sound like such a good idea to ask the QAs to remember what have been created in the old version of the app and know what have been missing or not during migration. Therefore, we would like to see whether it is possible to write unit testing for the database migration by the developers ourself.
So one of the first step is to generate test data. Notice that we can see all the previous version of the xcdatamodel from within our Xcode project, it seems that it is possible. In a nutshell, the question:
Is it possible to generate test data of previous version of xcdatamodel programmatically from within our current version of the app?
Please let me know what you think. Suggestions are also acceptable.
This is how we do it: we need to first get an URL to any model you would like to work with, and then create a managedObjectModel from it.
let oldModelUrl = NSBundle.mainBundle().URLForResource("CoreDataExample.momd/CoreDataExample",
withExtension: "mom")!
let oldManagedObjectModel = NSManagedObjectModel.init(contentsOfURL: oldModelUrl)
Referece: https://medium.com/#yzhong.cs/1d9f941b3168.
I received the source for an application from a guy to make a few changes in the app. The data model version was, say, App3. I had to add about 3-4 more data model versions in the process. So the active model version when I sent him was App7
I built the project (it was running properly on my mac)
I sent him the zip and when he ran, he got the error "Can't find model for source store"
I had him create the latest data model again, based on the data model App3 and make it the same as App7 and set it as the current version.
He still has the error and I can't figure out why? Are the intermediate data model version causing issues? Is it required to delete the sqlite file in the application support->iphone simulator folder? I am all over the place with this. Please help!
You need to have both model files and have some sort of versioning in place. The new model that you made will no longer work with his data file unless you specify to Core Data that you want it to update the data.
Versioning isn't that easy, but if your changes are small enough, you might be able to get away with automatic versioning. Read the guide and see:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html
In my understanding, it will. But really that is not the thing to do if ultimately the versioning is going to happen on existing live apps too.
For the light-weight migration to work, you need to send him all the intermediate versions of the model as well since the migration takes place stage by stage through all versions.
And you have to make sure that you have made no modification (even accidentally) to the base version App3.