iOS Core Data not performing Lightweight Migration - ios

I have a model with two entities, let's say A and B. The model is loaded from sqlite file downloaded from the internet (conforming to Core Data's sqlite format; i.e. Tables and columns beginning with 'Z').
I added a new version for my model; introducing a non-optional integer attribute with default value of zero to the entity B. For some reason, whenever I query that entity, I get nil result, and an error object of no such column ZVERSION (version is the new attribute name), even if I'm not using version in my query.
What probably can be wrong?

You can have a attributes named 'version' - it is reserved. From apple's docs:
Note that a property name cannot be the same as any no-parameter method name of NSObject or NSManagedObject. For example, you cannot give a property the name "description". There are hundreds of methods on NSObject which may conflict with property names—and this list can grow without warning from frameworks or other libraries. You should avoid very general words (like "font”, and “color”) and words or phrases which overlap with Cocoa paradigms (such as “isEditing” and “objectSpecifier”).
(source: https://developer.apple.com/reference/coredata/nspropertydescription)
'version' is a class method of NSObject used in archiving:
https://developer.apple.com/reference/objectivec/nsobject/1415151-version
Also creating an SQL file outside of core data and importing it is not supported. Apple does not publish it's sqlite format and you may not be conforming it. Perhaps Apple has some ZVERION column that you are unaware of.

Related

Remove or Add an attribute to CoreData at runtime programmatically

I have to create and remove attributes based on an api response in Objective C.
For example, Now my api response contains fields "facebook", "whatsapp" and "viber". But in future the reponse can add "youtube". Based on this response, I have to remove all the attributes and values of an entity "Social", and create Four attributes now and set values.
How to do that programmatically? Because the default *.xcdatamodeld file cant help me here, right?
Note: My project is in objective C.
The data model is mutable when the app starts-- you can completely build the model in code, and not use the model editor, for example. But as soon as you load a persistent store file, you must treat the model as fixed. Any changes after loading a persistent store will cause crashes. That means any changes would have to happen before calling either loadPersistentStores(completionHandler:) or addPersistentStore(with:completionHandler:).
Alexander's suggestion of optional attributes is a good one. If you need the model to be more dynamic, you would need to create a new related entity which would store the service name plus whatever information you need to save about the service. If you did this, your Social entity would have a to-many relationship to a new entity called something like Service. Service would have a string property called name that would have values like twitter, facebook, youtube, etc. It would also have whatever other attributes you need to save about the service.
You can create all 4 fields in advance and just make them optional and fill them depending on the server response. But you cannot add new attributes in runtime. Your *.xcdatamodeld file compiles into *.momd and it contains all the data to create tables in the DB since Core Data by default works with SQLite under the hood and it's a relational database management system.
To make attributes optional you should check that.
And then newly created objects contain nil as default values of object properties. So, in your case your "youtube" property of Social object will be just nil.

What is the criteria of compatibility between a store metadata and a managed object model?

My app uses CoreData framework and I want to check compatibility between a store metadata and a managed object model. I do it standard way:
BOOL isModelCompatible = [model isConfiguration:nil
compatibleWithStoreMetadata:metadata];
and it returns NO. However the entities in the metadata are the same as in the model. The same number of entities and each entity has the same name. However the model indeed has changed since the time the store was created using this model, I removed a couple of attributes in one entity. And I'm wondering if that is enough for a model to become incompatible with the store metadata. I took a look into the official documentation and it says:
This method compares the version information in the store metadata with the entity versions of a given configuration
And the problem (as it often happens when I read Apple's docs) is that I'm not quite sure what exactly this phrase means. So can anyone explain more regarding that topic? How CoreData decides if a model is compatible or not to a metadata given that enteties in the metadata are the same as in the model?
A model is incompatible with a persistent store any time there's a difference that affects how data is stored in the data file. Removing attributes would qualify, since that change would affect how data was saved in SQLite. Some changes don't lead to incompatibility-- for example, if you changed a relationship from optional to required-- because the data file would be the same either way.
If you want to get the exact details, look at the versionHash property of NSEntityDescription and related classes. That will tell you exactly what's used, and anything not mentioned doesn't affect compatibility. For example on NSEntityDescription it includes
The values which affect persistence are: the name of the entity, the version hash of the superentity (if present), if the entity is abstract, and all of the version hashes for the properties.
To continue from there, look up the same property on NSPropertyDescription and its subclasses.
Core Data's model migration gets past the incompatibility by updating the persistent store to match the new data model. Often this can be done automatically, as with lightweight migration. Removing attributes would allow lightweight migration.

Core Data entity naming convention

Is there a convention for naming Core Data entity? The argument I heard for not prefixing Core Data entity is because there is no chance they will be collision since they only need to be unique within a model, which is not true because the NSManagedObject subclass generated may still collide with existing Objective-C classes.
So it seems logical for me to do two things to Core Data Entity: Prefix it with my project class prefix, and suffix it with Entity. This way, I know it's a Core Data entity, and its name will never collide with any other classes.
I have actually seen both prefixing with the project class prefix and without. I have never seen a suffix being added. I prefer without the project prefix, since if you have a remote database that you are syncing up with, I would use the same entity names. And then if you ever release a public API, do you really want your project prefix all over the place? For example, Stripe's entities are Customer, Card, etc. They use the prefix in the unique identifiers, which I like. Also, if you are using the project prefix for your other classes, you do not run the risk of overwriting, like you mention in your OP.
From here https://developer.apple.com/library/archive/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
You cannot give an accessor a name that begins with new. This in turn means that you can’t, for example, declare a property whose name begins with new unless you specify a different getter
And as far as I know also copy keyword cant be used as suffix.

Core Data - Fetched Property with bitmasks and abstract entities

(Data models renamed to preserve anonymity. :D )
So, I have a situation where on a Thing, whose configuration is defined by its own special object, though that object can be one of 2 class types.
I would like to be able to do Fetch Requests on this model by asking if the Thing's Configuration is of a certain known subclass, and if so, does it have a few specific flags set in its bitmask. If this is true, I define this as being "not special". Therefore, if it also has the other type of subclass as it's Configuration, it is also "not special".
My questions are these:
Have I got my syntax right? (In my app I get faults)
Why does it want to return an NSArray? (I thought its traversing a to-one relationship on a single object. How would I therefore interpret this NSArray?)
I should add that I only know Core Data via MagicalRecord more or less. And I use mogenerator too.

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