Lightweight CoreData Migration with CloudKit - ios

Does Core Data handle simple data-model additions like a new attribute w/out any further "help" from me? In my case, I have an app that uses Core Data for varius things related to a users profile. I have an existing data-model entity called Profile that I want to add 2 new attributes to:
hasPublished: Boolean
lastDetail: String
So, does this cover my case? I'm not re-naming anything, just adding.
One more caveat, I'm using a NSPersistentCloudKitContainer as info can be shared across the users devices.

Yes it can handle, but there is some little work from your side.
You need to create new version of Core Data model.
Call initializeCloudKitSchema() so changes are uploaded to CloudKit.
Don't forget to deploy changes to production from CloudKit dashboard.
Remove initializeCloudKitSchema() when deploying your app to AppStore.

Related

Apple's Cloudkit Migration

This may be too simple of a question for SO, but for those that have used both Cloudkit/Core Data, do you have to migrate data in Cloudkit similar to Core Data if you make any changes to the database itself?
For example, if you adjust your Core Data model at all, you have to go through a migration process in order to make sure the App still works appropriately. Is Cloudkit the same way?
From my understanding there is no migration process necessary (judging when I make changes and run them in the simulator just fine), but I want to confirm before I upload to the App Store. Thanks for any help!
This is covered in detail in the Deploying the Schema section of the CloudKit Quick Start.
The quick summary is that you do need to push changes to production using the CloudKit Dashboard. But keep in mind that you can only make limited schema changes so plan ahead well. You can add new fields. You can add new record types. You can't delete anything.
Keep in mind that users of your app will be using old versions as well as new versions. The latest version of the app will of course know about any new additions to the schema. Old versions of the app won't know about the new fields and record types.

iCloud and lightweight migration

I've a published app synchronizing a Core Data with iCloud.
I need to update the model adding two attributes and then populate these new fields.
I've tested lightweight migration locally and works fine, I can see the old data migrated into the new model scheme.
When I activate iCloud, old data saved in the ubiquity container doesn't sync with the new model schema.
What is the expected behavior?
Should I be able to sync data both on old and new model versions?
How can I achieve it and test this situation?
I've read:
CoreData versioning
and
Understanding Core Data iCloud Store Migration When Testing an iOS App Update
but, actually, I'm very confused.
What's supposed to happen is that iCloud data only syncs between devices that are using the same version of the data model. If you upgrade the app on one device but not on a second device, they won't sync changes until the second device also updates to the new model version.
If that's not what you're seeing, please add more details about the problem you're seeing.

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.

CoreData Update Database Leaving User Entries

First, Thank you for any help provided.
I have an iOS leveraging CoreData to retain various presentations, this data comes from a sqlite file and there is no server connection.
I will have to be able to provide App updates (via appstore), this update may add more data to the database.
The tricky part is that it can not simply overwrite the current database, there are a few user tables that I will not like touched.
Please provide any information I should consider when accomplishing this or any links are greatly appreciated.
Thank you.
Given your app has no server connection, you will have to rely on shipping data within the updated application itself. I would recommend using a plist file or define your own xml or json structure. You can then read this data to create/update core data nsmanagedobjects.
It looks like someone in the past was using plist->coredata on SO
Would you have relationships between user created data and shipped data?
If not, you might go the route of connecting two stored to the persistent store coordinator. The shipped store would be read-only. The store with user created data would be read-write. You can use this approach, too, if you have relationships between shipped and user-created objects, but it's a lot more complicated, since CoreData doesn't manage cross-store relationships for you, and you'll need to write your own logic (doable, but not straight forward).
If you need to have relationships between shipped and user-created objects, you can still ship a CoreData store. When the app launches for the first time (no user-created objects), you copy the store to the Documents folder and user this store to create your CoreData stack. User created objects will be added to this store. Once you have new 'shipped' objects (i.e. a new store in the app-bundle), you'll have to manually migrate that stores data into the store that the user has changed. You'll have to be able to find
(1) objects that need to be deleted
(2) objects that need to be updated (changed)
(3) objects that need to be added
If you mark your shipped objects with a special flag such that you can tell if it's a user created object or a shipped one, that would be doable. You also have to have some sort of ID to be able to tell which objects in the new store correspond to which ones in the existing (old) store.
You do not need to go the route of using plists. In fact, I'd recommend against it. You can easily open two stores at the same time. Either to use both stored, or just to migrate objects from one store to the other store.

iCloud, Core Data, migration and model mapping

As said in the Apple documentation
Schema migration using mapping models is not supported (lightweight
migration is supported).
I was wondering about options we have in case we need to deal with iCloud fonctionnality and mapping model migration... I know that I will need to change my Core Data model in the future to add functionalities to my application (and not only in a lightweight way). The thing is that I can't say now which new entities will be needed and which relationships with previous model entities will be set.
I was thinking about a sequence like that:
1 - Launching my app doing the mapping migration of my Core Data Model
2 - Letting it synchronized with iCloud
This will work if iCloud contains transaction log files adapted to the new Model. In case of old transaction log files (means adapted to the old Model) it will failed.
To avoid that I was thinking about doing:
1 - Launching my app letting it synchronized with iCloud
2 - Doing the mapping migration of my Core Data Model
3 - Removing old iCloud data updating it with the new one
This will not work if iCloud already contains updated transaction log files (means adapted to the new Model).
What I need is a way to check if transaction logs in iCloud are compatible with my current Core Data Model. Is there a way to do this ?
Thanks.
I would not recommend using Core Data with iCloud in a production app.
Its not stable enough. There is no simple way of checking if the app is en/disabled in the iCloud settings. And not to mention the problems that can arise if the user turns the iCloud support off after having it enabled.
Migrations are another issue. Lightweight migration works fairly well. But in order to make a mapping model migration work, you need to clean out/evict content from the existing iCloud "ubiquity folder", create a new one (with a new name) and change the value associated with NSPersistentStoreUbiquitousContentURLKey. And, you need to make sure that all clients do the same. Its complex but doable. Not sure if its worth it though...
Regarding the version compatibility issue, I've not yet tried it, but an idea would be to somehow get the store metadata from an iCloud transaction log and through the "compatibleWithStoreMetadata" method on ManagedObjectModel check if the version matches:
// Get current model
NSManagedObjectModel *myModel = [self managedObjectModel];
// Check compatibility
BOOL isCompatible = [myModel isConfiguration:nil compatibleWithStoreMetadata: metadataFromTransactionLogEntry];

Resources