Is CoreData migration proper in this scenario? - ios

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).

Related

Core Data Migration: How to populate new attribute?

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.

Core data with unique constraints and relationships-IOS

I have a core data design with multiple tables using relationships. My database is SQLite. For updates I import data from JSON and use this method:
[NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context].
I have added unique constraints in core data.
If I update an entity that is a relationship of another object it loses the connection.
Ex: Entity "person" that contains the one to one relationship to "pet_id". If I update "pet" it changes his id and "person" still points to the old id, so they are not related any more.
Is there a way to avoid this problem?
I don't think this is documented anywhere yet. Here's what it sounds like is happening:
You create a new instance. Your constraints mean that this instance matches an existing instance. But...
Your new instance has a nil value for this relationship. So...
The existing instance's value for the relationship is replaced by this new nil value.
To maintain the relationship, your new instance needs to already have the correct value for that relationship. You're essentially asking that the constraint matching system ignore the fact that the relationship value is different in your new instance, but to accept new values for other attributes.
I think what you're expecting is completely reasonable but I'm also not surprised that the current implementation doesn't support it. I recommend filing a bug with Apple about this, and investigating non-constraint based approaches to keeping your data unique.

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 ;-).

Default value to core data attributes (column) for iOS programmatically

Current Approach
I set the default value of the attribute directly to the data model file using the inspector.
Problem
I have enum for the values a particular attribute can take.
I am worried maintaining them at a later stage might be difficult.
Suppose if I decide to change the enum values, then I would have to manually go the inspector and change it.
Since I have quite a number of attributes based on enum values, it becomes difficult.
Question
How can I add default values to core data attributes programmatically ?
Is there any alternative to do this, so that maintenance would be easier ?
Everything you can do graphically in the Core Data model editor you can do using the classes Core Data provides for creating/introspecting a managed object model. For this use case, you can use NSEntityDescription to look up an entity, its properties or propertiesByName accessors to find the NSAttributeDescription for the attribute you're interested, and setDefaultValue: to do the same thing the Core Data model editor does.
You might find this the most appropriate way to do what you're looking for. Or, as #DimitryShevchenko notes, you can initialize values in your NSManagedObject subclass' awakeFromInsert method -- which way you choose might depend on your workflow or other requirements of your application.
You can subclass your NSManagedObject and set default values in awakeFromInsert
Related docs (see Object Life-Cycle)

How can a user add a new field to a table by creating a new attribute

I'm new to Core Data and I got stuck at this part of my xCode project.
I have created a core data entity "Person" and this entity has the following attributes:
name;
age;
birthday;
address;
and this attributes are getting displayed in a tableview. So far so good.
My problem is that I want the table to have an "Add Field" or "Add Row" cell so when the user wants to add more information in addition to these already created attributes he just clicks the cell and chooses the field name and type.
For example if he wants the person's "phone number" in the detail view of the table he names the new field "phone number" and chooses its type "number". Then he has an extra field where he can add the person's phone number.
How can I do this in core data? Is there a way for a user to manually add a new attribute to an entity and choosing its format? What is the best approach? Thanks.
You can't do exactly what you want with Core Data. Core Data can't change structure except if you make a new version of your design, but you do that in xcode.
But you can easily add another table called f.ex. information, which links to the person single connection and has the person linking back many to the information table.
This way, you can add as many fields and values as you want, of course all the extra fields you add would follow the same person, so if you want to use cellPhone field, you must add that to all.
I would recommend that you use direct SQL, and don't use Core Data. Core Data is not a database, it is an object store, and when you get better at iOS development, you will understand the difference, it is much bigger than you might think at first.
There is an excellent high level library for SQLite, called FMDB, you can find it on github here : https://github.com/ccgus/fmdb
Here you can do direct SQL queries like "Alter Table" and more on the fly, though what you are after isn't very simple, it could be real fun project to do.
Good luck with this.
I don't think this is directly possible in Core Data because its purpose is object persistence and you can't add new properties to objects dynamically. It could be faked to some degree using a to-many relationship to an "extra property" entity that had name, value (as string), and data type fields.
I believe your best option would be using SQLite in order to modify the table structure on the fly. (http://www.sqlite.org/lang_altertable.html)
My last company did something like this, but its not trivial. I don't have access to the code so this is more or less going to be from memory.
you provide transformable property in your entity (which will be a dictionary)
the model object has to provide the getter and setter for this that in turn drive the primitive methods to set/get an attribute
you provide a getter/setter along the lines of -objectForKey and -setObject forKey, which read and write values
when you are told to 'fault', you update the dictionary in the entity
In summary, maintain a dictionary of key value pairs. Perhaps you maintain a shadow dictionary that gets initialized and updated as needed. Its been around 4 years since I last saw this code so a little fuzzy on it. But you should get the idea. It was like magic - you can arbitrarily set any key/value pair (assuming string keys and NSCoding compliant values), and can always ask for the keys by asking the dictionary for its current set of keys.

Resources