Core Data Lightweight Migration Attribute Renaming - iphone

I know there is a lot of questions about Core Data Migration here in Stack Overflow but I couldn't find a solution for my problem.
I successfully did a Lightweight migration by renaming an attribute called "LastName" in the source model to "Lastname" in the destination model. I did step by step to achieve this and it works except the fact that all of the previous data on the column "Lastname" now is lost. The new renamed attribute exists, i can insert new Entities and the values of the new inserted entities are ok. The problem holds on the old data and the old attribute. As apple documentation says, I put the renaming identifier of the new renamed attribute on the destination model to the name of the old attribute in the source model by XCode interface or by code and none works. My data on the renamed attribute is always lost.
Is that expected or a miss something?
Thnxs,

I recently had this same issue where even with a renaming identifier, the data was being lost between the versions of my model. The model was jumping from Version 10 to 15 (with the change happening between versions 11 and 12), so the renaming identifier wasn't being pulled out, even when I included it in later versions. I also had the issue of how to deal with any later changes to the attribute.
In the end, I added the following code to the middle of the persistentStoreCoordinator method in the App Delegate (which is part of Apple's placeholder code when you setup a new project with Core Data added) before you connect the persistent store to the persistent store coordinator to perform lightweight migration:
NSEntityDescription *entity = [[[self managedObjectModel] entitiesByName] objectForKey:#"EntityName"];
NSPropertyDescription *property = [[entity attributesByName] objectForKey:#"NewAttributeName"];
[property setRenamingIdentifier:#"OldAttributeName"];
I then used this question on StackOverflow (CoreData how to detect new version of an app so I can update the model?), which describes how to store the CFBundleVersion alongside the persistent store. That way, if you update the attribute's name again, you can check the current version of the persistent store and you know the latest version of the store, so you can choose the correct renaming identifier on the fly.

Related

Core Data: Rename Attribute without having issues with users and their current data

I just want to rename and add attribute on my table for a new version of my app and I want to keep the data if the app was already installed.
Firstly I just set the options:
let options = [NSMigratePersistentStoresAutomaticallyOption:true, NSInferMappingModelAutomaticallyOption:true]
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options)
And I created a new version model, so if I rename the attributes and add another attributes to my table on the new model, do the app gonna keep the data ?
Per Apple's Core Data Versioning and Migration Guide's section on Lightweight Migrations:
If you rename an entity or property, you can set the renaming identifier in the destination model to the name of the corresponding property or entity in the source model. You set the renaming identifier in the managed object model using the Xcode Data Modeling tool’s property inspector (for either an entity or a property). For example, you can:
... Rename a Car’s color attribute to paintColor
Your code requests automatic lightweight migration. If you want to rename an attribute, migrating like that will not retain data for that attribute. All other data will be retained. Core Data would see it as deleting the old attribute and adding a new unrelated attribute.
If you want to rename an attribute and retain data for that attribute, you can't use automatic lightweight migration. You would need to create a mapping model to tell Core Data how to migrate the data-- specifically, to tell it that the data from the old attribute name should move to using the new attribute name. Once you have more than one version of the mode, you can create a mapping model in Xcode to set this up. The overall process is described in Apple's guide to model migration.

Adding a new entity to core data and populating some values

I am updating my core data model in the next version of my app. I have added a new entity for 'Departments', following the instructions here.
I would like to add some sample departments, both for new users and those who update to the latest version. My approach up till now has been to store a 'HasLaunchedOnce' boolean in [NSUserDefaults standardDefaults], which if missing adds some reference data and sets itself to YES for the next launch.
Should I add a similar flag (hasLaunchedV1.1Once) to add my departments? What other approaches could I take? My goal is to keep things as simple as possible for future versions. I would prefer to simply check if any departments exist, but of course this would fire if the user had deleted all departments manually.
Your approach is valid in principle.
Just take into account that the NSManagedObjectModel class has an instance method versionIdentifiers. You could also use this to find out what the latest version is.

CoreData Automatic Lightweight Migration Error

I am trying to use automatic lightweight migration in my app. I did the following steps:
Create new model version.
Edit new model version.
Set the options NSMigratePersistentStoresAutomaticallyOption and NSInferMappingModelAutomaticallyOption to YES upon creation of the persistentStoreCoordinator.
Setting the current version to the new version.
and got an error: reason = "Can't find model for source store"
I've tried Product->Clean to no avail. Ideas?
Edit: I forgot to put in the model details.
I have two .xcdatamodel files. The first has two Entities, Event and Venue. The second has one Entity, EventDate. I no longer use the second .xcdatamodel in code but I did leave the file in the project. I am trying to add a new entity Update to the first model file.
The error ""Can't find model for source store" means that the .xcdatamodel file originally used to create the existing persistent store cannot be found.
Supposed you started with these two model files (the version number is the version of the app not the data model):
oneEntityModelv1.xcdatamodel
twoEntityModelv1.xcdatamodel
… and you want to migrate to this data model:
threeEntityModelv2.xcdatamodel
The v2 version of your app would have to include all three files for the migration to take place.
The problem was that we were using the mergedModelFromBundles to instantiate our model. Apparently that doesn't play well with automatic lightweight migration. The fix was to
delete the unused xcdatamodel.
delete the database file.
version the remaining model.

Out-Of-Memory while doing Core Data migration

I'm migrating a CoreData model between two versions of an application. I was storing binary data as blobs in the previous version and I want to take them out of the blobs for performance. My issue is that during the migration it seems that Core Data loads everything into memory which leads to Low Memory Warnings and then to my app being killed.
Apple documentation suggests the following :
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmCustomizingTheProcess.html#//apple_ref/doc/uid/TP40005510-SW9
However, it seems to rely on the fact that the large objects are applied different mapping.
In my case, all the objects are basically the same and the same mapping has to be applied to each of them. I don't see in this case how I could apply their technique.
How should I handle a migration with very large objects ?
I'm guessing that you have a bunch of changes you want to make in addition to pulling the data out of blobs. My suggestion is to do the migration in a few stages. I'm kind of thinking out loud here, so it might be possible to improve on this. This requires you to be using SQLite.
To make this work, you're going to have three versions of your model:
The original model
The model with the attribute removed (and possibly with a special unique ID added--see below)
The model with all of the changes you've made, including the addition of the new entity and relationships replacing the attribute
The reason to do this is that the transition from version 1 to 2 should be doable with an automatic lightweight migration. In that case Core Data doesn't need to load anything into memory--it just issues SQL statements to make the changes directly on the database.
So, you start by setting up your persistent store coordinator using the old model version. Once you've loaded the data, go through all of the objects you're migrating, extract the binary attribute, and write it to disk somehow. You can use a fetch request with batching and regular autorelease pool draining to make sure you don't use up too much memory for temporary objects. Store the data into the directory you get with NSCachesDirectory. You'll obviously want to store the data in a way that lets you relate it back to the object's managedObjectID.
Then, you shut everything down and ask Core Data to migrate the store from version 1 to version 2. See this link for details. Open up the store with version 2.
You might have to add a step where you assign some sort of unique ID to each object, because I'm not sure if Core Data maintains object IDs when it does a non-lightweight migration. If you need to do this, your version 2 model would add a new attribute to the object you're taking the binary data out of that would be either optional or have a default value set. Since lightweight migration shouldn't change the managedObjectIDs, you could at save the mapping of your new unique ID to the managedObjectIDs you saved along with the binary data two paragraphs ago.
Save the data and close the store.
Open the store and do a migration from version 2 to version 3, which should basically be the code you already had written before you posted the question. Once the store is open, add all of the objects you saved from the version 1 store and set up the relationships using the data you saved along the way.
Simple, right?

iPhone Core Data how to update the NSManagedObjects .h & .m after making changes to the data model

I have made a data model, and now I've made a 2nd version. All of the generated NSManagedObjects mapped to that data model are all version 1. Is there some way to update them to v2 without deleting them and then saying having them generated again?
If you have two versions of your model you need to either create a mapping model between the two versions or you need to turn on automatic migration if the changes are simple enough for Core Data to resolve.
I would suggest reviewing Apple's documentation on the subject of Core Data migration.
update
I'm using auto migration. But say I have a person entry in v1 that just is last name and first name. In v2 I added date of birth. After making the data model of v1 I told Xcode to generate the NSManaged objects (in this example Person.m) now I've made v2 model with auto migration set up. How do I update Person.m to add DOB without deleting and then adding it again, or adding DOB manually?
The NSManagedObject will already have it in there, if you want to add the properties (as opposed to just the attributes) then just type in the two lines of code into your NSManagedObject subclass.
update
Other option, which I highly recommend, is to use mogenerator instead of the built-in code generator. mogenerator will keep the files up to date after model changes.