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.
Related
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.
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).
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.
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.
I've looked all over for an answer, but it seems like I'm missing something obvious. I've made a rather complex Core Data app before, but the answer to this question has eluded me for the past few months.
Here's the problem:
1) I have about 20 entities in my Model.
2) Some of these entities have user-editable objects, others have pre-loaded data
3) I would like to know if it's possible to update the pre-loaded entities with each new app update.
I know I can do this the "manual" way by specifying each updated attribute, but this is way too cumbersome. I want to just update all the pre-loaded entities once the user opens an updated version of the app. I don't want to touch the user-data.
Thank you so much for your help!
You could have a version number field in your schema which you can use to associate a version number with each record. If it has a value, it's a preload. Then for the preload stuff just insert the new data when the app opens, and then ignore/delete the old. Seems simple enough.
UPDATE:
The other alternative I believe is to separate your preloaded data into an entirely different data store. I have an app wherein I do this by delivering my preloaded data via a custom SQLite file, and user data in a CoreData store. I can do this because my preloaded data is read-only, which saves me from needing to copy the SQLite file into the documents directory. What this means is that, at every update, the new data file automatically overwrites the old by virtue of the app installation. The user's data is maintained as it should be.
Of course if your preloaded data is not read-only, then there's no way around the need to write code. In this case there's not much I can do for you, not having any more details about your problem.