Core Data Migration: How to populate new attribute? - ios

I have what feels should be a simple problem, but cannot find a simple answer.
I have a simple migration, I just need to add a synthentic property for use in a Fetched Results Controller. This new property is just a BOOL (used for sorting) that is derived from another property.
Example:
var title: String? // "engineer" | "accountant"
var hasTitle: Bool // title != nil
Simply, I need to add the new field hasTitle (which seems trivial), and the populate it once. I want to keep this code out of my normal app logic, so I don't have to code in a history of all my schema changes.
Is it possible to do a lightweight migration followed by an isolated, one-time mass update, or do I have to do a custom / heavyweight migration.
And secondly, if I need to do a custom migration, are there any mitigating techniques - can I use a Value Expression "Function" to do initialize the above (title != nil)
What I want to avoid is having to set a NSUserDefaults flag for a migration, and check that on every launch. I would like to contain the complexity of the migration to migration specific code and not pollute the regular app logic.
Thanks!

Is it possible to do a lightweight migration followed by an isolated, one-time mass update, or do I have to do a custom / heavyweight migration.
Both are possible. I'd go with a custom migration, simply because it'll save you from having code that needs to check on the new attribute every time the app launches. For a simple change like yours it's pretty straightforward, although it's uncharted territory for most iOS developers. I recently described the process in detail in another answer.
If you don't go with a custom migration, you'd need to make the new attribute optional and then add some custom code to check if a value exists and assign one if it doesn't. It might seem simpler, but that's just because it probably seems more familiar. Core Data will save you from needing to insert this kind of check in your app launch process, and ensure that your conversion code runs only when a conversion is actually needed.

Related

Cannot retrieve renamed attribute's value in Core Data

In my iOS app's core data model, I had an Entity called Fish with a string attribute called mainText. I renamed this attribute to questionText. The CRUD operations works fine for the new data. However, I cannot read the old data, the one written when the attribute was called mainText. Here is what I have tried:
Renaming it back to mainText, it does not work.
Adding a new attribute called like the old one, mainText, it does not work.
How would you proceed to retrieve the old data's values (the one wrote when the attribute was still called mainText) ?
If you want to rename an attribute and keep the old data, there are two things you need to do:
Create a new version of the data model. You probably already did this, or your app would not have been able to load the old data at all. If not though, make sure you keep your changes in a different version of the model file. The old model version needs to be available or migration won't work.
On this property, set the "renaming identifier" field to contain the old name of the attribute. This will tell Core Data that it should migrate values to the new attribute name. Without that it can't tell if you want to rename the attribute or if you want to delete the old one and add a new, different attribute. In the model editor, you'll find this on the right when you select the new attribute:
You may find Apple's guide to lightweight migration to be useful too.

Coredata migration: change value type

I've an already shipped application that use Coredata so save all the data. My model defines a value of type BinaryData and I would like to change the type to Integer.
Currently that field is unused, but his type is incorrect. Can I migrate my store without pain? I've tried some approaches but none of them actually worked.
Any ideas?
The proper way would be to use a mapping model, but I think there is a much more practical solution for you. Because the field was never used, just delete it. The overhead is practically inexistent.
Now all you have to do is lightweight migration:
Create a new model version.
Add the Int attribute, delete the old one.
Change the active model version to the new one.
Change the options in your call to addPersistentStore to include
NSMigratePersistentStoresAutomaticallyOption
NSInferMappingModelAutomaticallyOption
Change your code to use the new attribute
Eliminate all potential uses of the old attribute from your code
Test it thoroughly before you upload ;-).

Is CoreData migration proper in this scenario?

I have an app that utilizes Core Data. One of the Core Data entities is an NSInteger that represents an enumeration. In my next revision, the enumeration values have changed and I need to remap the old enums to the new enums.
Is Core Data migration an appropriate approach in this case, as the model has not changed, just my interpretation of the data. I've attempted to implement a migration policy, but I can't get it working (my migration policy never executes).
Thanks!
--John
If you've added a couple of new values to the end of the enum, then I wouldn't migrate anything, just make sure the old numbers still equal the old values.
If you have changed the value that each enum represents, then you should not have used an enum in the first place as enums are never supposed to change. In this case I would add a new column or relationship and probably better not to use an enum this time.
Leave the old enum in the database structure, but have your subclass of NSManagedObject handle updating both values whenever either one changes (or something like that).

Updata NSManagedObject class definition from datamodel

Being fairly new to CoreData (Started playing with it 2 hours ago ;) ) I keep wondering if there is an easier way to do things.
I'm currently using a DataModel to create my persistant objects code.
However when I keep adjusting things like number of attributes sotred, the name of the fields etc... and that leads to having the update the code of my NSManagedObjects.
I found that you can create the classes with the attributes already implemented, however, I haven't figured out how to update the code of an existing class other than adding it by hand or creating a new object and overwriting the existing one? (or how do other developers do it)
I'm aware that the answer is surely: "there is no better way" but I still have to ask.
Check out mogenerator.
This command line utility will generate two class files. A machine generated _MyClass header and implementation, with all code for your model. It will also create a subclass MyClass where you can add your own customizations.
Each time you regenerate your code, only the machine generated _MyClass files will be updated. All your changes to MyClass will be preserved.
If you can not use mogenerator, you can use the native code generation. Make sure you regenerate the code every time. However, that will erase any changes.
So, you have two very viable options.
Write a category on the entity class. You can do just about everything you want with a category.
Subclass from the entity, and put all your code in there.
Personally, I prefer using categories unless I absolutely must inherit... which is extremely rare.

EF Code first database/table initialization - WHEN does it happen?

My application is using EF code-first design and all generally works very well.
Via a private configuration file, I can specify how I would like EF to handle changes to the db schema, and so create/recreate the relevant tables as desired - the options are "never" "create", "always", "onSchemaChanged" and (for the future) "onSchemaModified".
This works well - but I am getting lost in a couple of places .....
During development, I would like to use the hook as described in
"Database in use error with Entity Framework 4 Code First" - but this seems to execute on EVERY run of my program"
public void InitializeDatabase(Context context)
{
context.Database.SqlCommand("ALTER DATABASE Tocrates SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
_initializer.InitializeDatabase(context); // Maybe this does nothing if not needed
context.Database.SqlCommand("ALTER DATABASE Tocrates SET MULTI_USER")
}
So .. to real my question: Is there an override that I can use to detect whether EF will ACTUALLY be trying to modify the database, so I can set this SINGLE_USER stuff when needed? And if so, can I detect the reason EF it is doing so (see my list of options above) so I can log the reason for change?...
All help and suggestions are very much appreciated.
Unless you have set the database intializer to null initializers run always once (per application lifetime) when you are using a context for the first time. What then actually happens depends on the initializer (your inner _intializer):
For DropCreateDatabaseAlways and CreateDatabaseIfNotExists it's clear by their name what they do.
For DropCreateDatabaseIfModelChanges there is only the question if the model changed or not. EF detects this by comparing a model hash with a hash stored in the database. You can check this yourself by calling...
bool compatible = context.Database.CompatibleWithModel(true);
...within your custom InitializeDatabase and then decide based on the result if you want to send your SqlCommands or not. (Don't call this with a self-created context because it will cause the database to be intialized first before the model compatibilty is checked.) The parameter bool throwIfNoMetadata (which is true in my example) causes EF to throw an exception if the model hash in the database does not exist. Otherwise the method will return true in that case.
For a custom inner initializer: Whatever your code will do.

Resources