Ensembles and Core Data Light Migration - ios

I am currently doing some tests with Ensembles, specifically testing Core Data light migration.
My current configuration is as follow:
Device-A running my app with data model 1
Device-B running my app with data model 2
data model 2 is based on data model 1 with one additional string property, which is optional
My scenario is as follow:
At the beginning, running my app with data model 1 on both Device-A, and Device-B, everything synced fine using Ensembles (iCloud configuration)
On Device-B, install and run my updated app using data model 2
On Device-A, keep running my old app using data model 1, and add a new record
The result: the new record added on Device-A is uploaded to iCloud and then synced to device-B
My question: can I configure Ensembles to prevent it from uploading changes to iCloud in case that related data model is not the latest one? (i.e. in my case, Device-A uploads an object based on data model 1 while iCloud is already based on data model 2)
Thanks in advance!
UPDATE 1:
Drew, thank you very much for your answer. I definitely agree that uploads can't (and probably shouldn't) be prevented as Ensembles is a decentralised, peer-to-peer system.
In the ideal case, I would like that the device with the new data model will ignore data that is based on the old data model. (in a similar way to the existing behavior where the device with the old data model will ignore any data based on the new data model). Is that supported?
If not, please consider the following scenario as an example:
The old data model has an entity called 'Book' with two properties: title, and author (both fields are non optional)
The new data model has a new optional property called titleFirstLetter that should hold the first letter of the title field.
Currently, when Ensembles is not involved, I have full control when saving new NSManagedObject to the persistence store. Therefore, the updated code of my app which responsible for adding a new book, will make sure to extract the first letter from the title field and save it to the new titleFirstLetter property. (i.e. a book titled Catch-22 will have C in the titleFirstLetter property when book is saved).
In addition, when light migration occurs on the core data stack, I detect that, and perform a one-time procedure where I iterate all existing books in the database, and set the titleFirstLetter according to the title value. From this point and on, the database is consistent and valid, while the new code will ensure that future books added to the database will keep database valid.
Regarding Ensembles, if I don't have any control on old data coming from devices with older data model, how can I fill the new property of titleFirstLetter, if my code is never being called?
Thank you for your kind assistance!

You can't prevent it, no. Ensembles is a decentralised, peer-to-peer system. There is really no way for one device to know the current state of another device, so you couldn't prevent an upload.
The updated device should be capable of handling the old data from the other device. The device with the old model will ignore any data based on the new model, until it too is updated. Then it will merge all of that ignored data.
It is best to avoid migrations where possible, and stick to simple stuff like adding properties or entities, rather than tricky refactors. If you need to make a lot of changes, consider simply starting with a new ensemble (e.g. change the ensembles identifier).

Related

Deleting entity from core data

After making a rough version of my app, I am now looking to make the finalised version which is better and has many improvements. This involves changing the attributes in the entity holding the timers.
Having put the app on TestFlight already, some of the downloads will have data stored already. I am going to delete the current entity and make a new one. Will this cause any problems if data is already stored for the attributes within the entity that is going to be deleted?
IMPORTANT: I do not want to keep any of the data in the entity to delete and I want to delete it all, so when users update all their data will be gone (this is fine as I distributed it only to people I know e.g. family/friends), who have given some feedback.
Any help is much appreciated. Thanks in advance.
CoreData is very pedantic. Once a store has been created based on a data model, you cannot use it with a different model unless you do some kind of migration. Different in this case means almost any change to the model. Certainly changing the attributes for an entity, or adding a new entity, will be sufficient for CoreData to throw an exception if you try to open the existing store with the new model.
If you want to preserve the data in the existing store, you would have to do a migration, but since you do not want to preserve that data, it will be easier to delete the store when the new version of your app is first run, and let CoreData create a new store using the new model.

Will adding a new fetch request template to Core Data break previous versions of model?

I want to add a new fetch request template to a core data model. I know I could do it programmatically, but all the other fetch request templates are present in the core data editor and it makes sense to add the new one alongside them.
My question is, since this is part of a point release for an app that has been in the store for quite some time already, is there any chance this will break existing installs? Does it count as some kind of migration, or not? Obviously we will QA it, but I'd like to know the answer in advance, and googling has been fruitless so far.
It's not stated explicitly anywhere, but in the NSManagedObjectModel documentation, it says the following:
Changing Models
Since a model describes the structure of the data in a persistent store, changing any parts of a model that alters the schema renders it incompatible with (and so unable to open) the stores it previously created. If you change your schema, you therefore need to migrate the data in existing stores to new version (see Core Data Model Versioning and Data Migration Programming Guide). For example, if you add a new entity or a new attribute to an existing entity, you will not be able to open old stores; if you add a validation constraint or set a new default value for an attribute, you will be able to open old stores.
It doesn't explicitly mention fetch requests, but these don't have anything to do with the schema, so I think you'll be fine.

iOS: DDL Commands during runtime

i want to add tables/columns to a database during runtime.
Currently I'm using Core Data.
I know that there's a possibility to do so in XCode (add new data model version), but I definitely can't use that way, because I receive the database schema from a web service.
Is there any good possibility to run ddl commands during runtime when using Core Data, or is it just possible with directly using sqlite (or a wrapper/ormapper)?
If it's better to use a wrapper/ormapper please give me some suggestions about which should be used in this case.
Workflow should be:
start app
check if database is up to date
if new version of schema is available from a web service do DDL commands
continue with app workflow
PS: Please no answers which describe alternatives modifying the schema with XCode!
Can you modify the Core Data model at run time? Yes...but, it probably won't work the way you want it to work.
Core Data's API makes it possible to construct or modify every detail of a data model at run time. Xcode's model editor is a convenience, but you could skip it and do everything in code if you wanted. For example, NSEntityDescription's properties attribute (which covers both attributes and relationships) is writeable. You could create a new NSAttributeDescription and update the entity's properties to contain it. Bang, you just added a new attribute to the entity. Similarly, NSManagedObjectModel's entities property is writeable, so you could create a new NSEntityDescription and add it to the model. That gives you a new entity, created at run time.
But, and it's a big one: you can only do this before you load the data store. Once you load your persistent store, altering the model will throw an exception. When Core Data loads a persistent store, it compares the model file to the model used in the store file. They must match, and you can't do anything to change this fact after loading the store. Once you load the store, the model is fixed.
What's more, even if you modify your model before loading the persistent store, you can only load persistent stores that match the current version of the model-- unless, that is, you also write code to migrate the persistent store to the new model. How hard that is depends on the nature of the changes. At a minimum then, you would need to make any changes before loading previously saved data, and then also arrange to do model migration to update the persistent store to use the new model.
With Core Data the model (schema) and data are stored separately and matched up when the store is loaded. That's not how SQLite works internally but it's the approach that Core Data enforces.

How to upgrade iOS Core Data model and data

I've got an iOS app which uses Core Data (SQLite on the backend). It only has one entity, 'Item'. There is a SQLite file bundled with the app, with hundreds of items pre-added, so when the user downloads the app from the App Store it already has the data.
The only entity has a BOOL favorite attribute which the user can alter, used -of course- to check if an item is among the user favorite items.
I'm planning to publish an update of the app with more items pre-built in the app bundle (a new SQLite file), but I want to keep the user favorites. As well, in this version my Core Data model will suffer a few modifications (I need some new properties in the 'Item' entity). The new set of items is a superset of the old items (an item in the old version of the app shall be in the new version, always).
I've been struggling with this a lot and I can't find a solution to this. I'm able to upgrade the data model introducing new properties into my entity while keeping the user favorites (performing a so-called lightweight migration, but then I'm not able to merge old and new items. On the other hand, I'm able to get the new pre-added items, but then the favorite-related data is discarded.
Any hint? Thank you all in advance
I finally managed to solve the problem.
I've got two NSPersistentStoreCoordinators, two NSManagedObjectContexts and two NSManagedObjectModels in my app delegate: one set to use in the application (the updated one) and another set pointing to the old store. In my app delegate didFinishLaunchingWithOptions: method I load all the user's favorites from the old store and save them into the new one. That's the only point in the app where I touch the old store.
Thank you all anyway!
I would suggest creating a second database with your new stuff in there, but without favorites. Then you pull favorites from your old database and insert them into your new one. Remove the old database and replace with the new one. That seems like the most straight-forward solution. There may be functionality built into Core Data for these situations, but chances are this is easier.

iOS avoid using old core data model

I have a project on that uses core data and I had migration implemented for whenever I needed to make a small change to my model. However, in the past week I've had to do a complete overhaul of my model and the way it reacts that I can not migrate any of the data.
With this I was wondering if there was a way to check what version of the model I have in the device and perhaps, through code, delete it and completely create a new one from scratch. This may not be the most preferred way, but if anyone has any better solutions than I am open to suggestions.
Two reasonable options:
Use NSManagedObjectModel's usual versioning mechansim. Bump up the version of your new model. Core Data will automatically check to see whether a given store is compatible with one of the models in the app before it opens the store; if not, it'll give you an error when you try to add it. So if you don't provide the old model in your app, Core Data won't use any old stores. You can respond to the error by deleting the store. You can also check a store before adding it using -isConfiguration:compatibleWithStoreMetadata:. See Initiating the Migration Process for more.
Use a different name for any stores you create using the new model. It's not uncommon for an iOS app to use just one or two stores with specific names. You probably know the name you used for the store in previous versions of your app; switching to a different name gives you an easy way to know whether a store is compatible with the old or the new model.
If you have changed the properties so that some of the old properties don't exist in the new model, try using the respondsToSelctor: method to check if an entity responds to an old selector. If it does, you have the old model.

Resources