iPhone Core Data Migration leads to binary data loss when allow external storage selected - ios

I am trying to add a new model. The new model will have one new entity and a relationship from a current entity to the new one. I have created the new model and set it as current. Although everything works fine and i can access the new entity, some 'binary data' entries are nil.
I tried adding a mapping model and then migrating, but the results are the same. The 'binary data' stored are UIImage. Some small thumbnails are being migrated. None of the full size UIImages are being migrated.
I am thinking that maybe the entities that are automatically stored externally (due to their size) are not being migrated.
UPDATE
I build a small project that stores image collections. I tried to create a new model and everything worked fine. Then i compare the new project with the old one and the only difference was the "Allows External Storage". I selected this option on the new project, created a new model and the pictures were missing.

I am thinking that maybe the entities that are automatically stored externally (due to their size) are not being migrated.
That's exactly what happens. It's an Apple bug. Binary data over a certain size gets stored by Core Data in an external support folder within the Documents folder. For some reason, during a migration, Core Data simply wipes that external storage folder - gulp! So thumbnail image data migrates ok as it's not stored externally to the SQLite DB but larger external binary files just get deleted.
The solution I’ve come up with is when your code initializes a persistent store coordinator for your Core Data model run a few checks before attempting automatic migration. Check whether the new model is compatible with the current stored model. If it’s not then you know that Core Data is about to migrate your old model to your new version and in doing so will wipe the external storage folder. Before it does so simply move the external storage folder to a temporary location. Once the migration has completed replace new empty external storage folder generated by Core Data.
I've documented the full solution with source code in this blog post:
http://www.nickkuh.com/iphone/core-data-migration-woes-with-binary-data-and-external-storage-data-loss/2012/06/

Related

I didn't migrate NSPersistentStore in new version, can I recover with an app update?

long story short, my Core Data schema was changed, and my app was submitted to the app store, which caused everybody who updated their app to crash. The crash is caused by a missing NSPersistentStore due to not migrating the data model properly.
I still see the .sqlite and associated database files in the documents directory, and if I downgrade to the older version everything works fine with all data. My question is, can I recover from this with an app update by somehow migrating the existing NSPersistentStore and adding it to the NSPersistentStoreCoordinator?
EDIT: so I didn't actually change my xcdatamodel myself, hence, "long story short". However, I did remove XMPPFramework from my project and I have a feeling this might have caused the core data problem.
EDIT:
I didn't make any changes directly to my data model, but I have pinpointed the problem. I was retrieving my NSManagedObjectModel using [NSManagedObjectModel mergedModelFromBundles:nil] which merges ALL data models present in the bundle. This included all data models that came with the XMPPFramework, and now that the framework has been removed, the NSManagedObjectModel that is passed into [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel] is different, thus causing the crash.
I resolved the problem by using FMDB to fetch the contents of the existing DB, then created a new sqlite file and pointed the persistent store coordinator to that new sqlite file. Then I just inserted all the existing data into the new database by looping through the existing data and creating the appropriate NSManagedObjects. I also stopped using mergedModelFromBundles to retrieve my data model and instead use initWithContentsOfURL.
Actually you might have updated xcdatamodel somehow after first version, And you didn't created a new xcdatamodel model for second version. So on second update it's crashing.
NOTE: After first version release, you have to create a second model version of xcdatamodel.
To create second model version of xcdatamodel -
1. first you have to select Model.xcdatamodeld -> Go to Editor on menu -> Add Model Version. Here you have to name a model version and based on which old model you want to create this new one.
Now whatever changes you want to make you should make on new model version xcdatamodel.
2. You can see on image, I've given a new name to my new model 'Model2.0', which is based on my previous 'Model'.
New model will work exactly like your old model, Additionally changes will be made on new model won't affect to your old model. So it won't crash your app after update.
3. You have to select your new 'Model2.0' as a default working model. For that please consider the below image.
4. Here you can see there is 2 model now. Please select 'Model.xcdatamodeld' a main model, and open it's 'File Inspector' on right side - as opened on above image.
5. There is 'Model Version' field on the right side, Which indicates the 'current' model selected on this project. Please select the new model 'Model2.0' for your updated version. Now you can run and it will work fine onwards.
NOTE: Please make sure now whatever changes you will make, you will do it on your new model 'Model2.0', So it won't conflict with your old model. You have to make new model each time, if you want to change configuration of xcdatamodel on updated app version.

How to update the Pre-Populated CoreData SQLite file?

I know I can set a Pre-Populated SQLite file as CoreData persist file. But When I update my app version, and the Pre-Populated Data need be updated, I wonder if there has another way to do that except CRUD the new Pre-Populated data by codes after the new version launch.
You can pick the pre-populated sqlite file as a source file in the persistentStoreCoordinator but keep in mind that if you update de data, the user generated content will be lost...
In one of my project I have also faced a similar problem. My approach was that.
Create 2 configuration in Core Data. One for static data and second for dynamic data.
While configuring your persistantCordinator set two separate SQLite files corresponding CoreData configuration.
There is not extrac coding effort other than setting up two separate DB in persistantCordinator level. Everything else will be managed by CoreData like. Reading, Writing etc.
For static database keep a DATABASE version of your own. When your static data changed increase this database version and in App launch check the existing database version with this new version. If new version is greater copy and replace the existing static database.
With this approach you have the following benefits.
You wont lose your dynamic data.
You need to change the static database only when there is a change in static data.
Hope this helps.

Magical record , core data migration for released app in Appstore

Our app got released in app store and now I wanted work on next version. Here I might add property and entity to current model .
I am using core data with Magical record .
I need help on core data migration with magical record .
I am already using [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:#"xxxxxxx"]; in app delegate .
As per my understand MR will take care of migration if we use above method .
Should I need to do any changes in Model.xcdatamodeld like adding model version (Editor->Add model Version) .
Please help me how can migrate core data.
To build on #casademora answer who obviously knows a lot more about MR than me, here is what got stuff working for me. The key is reading the Apple docs as suggested.
highlight your existing .xcdatamodel and then click Editor > Add model version > name it with an increment from your previous (i.e. if "myapp" use "myapp 2" as suggested in xcode.
make your changes on the newly created .xcdatamodel.
highlight the parent .xcdatamodel and then on the File Inspector on the right of xcode select your new version as the current Model Version.
NOTE: This step is only required if doing more than a Lightweight Migration. Select File > New > File > Core Data > Mapping Model. Choose your original as source, new as target and then save in the same folder as your .xcdatamodel.
make sure you are using setupAutoMigratingCoreDataStack or setupCoreDataStackWithAutoMigratingSqliteStoreNamed of course
test by downloading the app from app store and opening it, then closing and running debug over the top. You should not get any "Removed incompatible model version" messages (i.e. all persistent data should be in place still) or any other errors.
That method simply enables auto migrations to happen when you have multiple versions of a data model in your application bundle. To add a new version of a data model, you'll need to select your data model in Xcode, and in the menu select Editor -> Add Model Version... From there, Xcode will do the proper setup for you. This is also a fairly simple idea, it creates a new data model file, that starts off as the contents of your current data model file. From there, you can change and edit your data model as you see fit. Be aware that only simple changes are "automatic". Adding a new property is valid if it has a default value. Adding a new entity falls into the automatic category as well. I suggest reading more details about Core Data Migrations from Apple's Official Documentation

How to recreate xcdatamodeld

I am maintaining an app that has been live on the AppStore for a few months. My app uses Core Data and somehow I managed to lose all previous versions of my xcdatamodel.
I am hoping to perform a lightweight migration. All I need to do is add one attribute to one of my entities. So, I have followed the correct steps of creating a model version.
Apparently, I modified the original xcdatamodel. So, when I test the migration, it fails with an error Code=134130 "Can't find model for source store". Since the app runs fine when there is no sqlite file on the device/simulator, I've concluded that the original xcdatamodel has been modified.
Assuming my logic thus far holds, how can I create an xcdatamodel that will match the sqlite files on my users' devices. It's not acceptable for them to lose their data.
Thank you.
According to Apple's Core Data versioning guidelines, two versions of Core Data are treated as being identifical if:
For each entity the following attributes must be equal: name, parent, isAbstract, and properties.
className, userInfo, and validation predicates are not compared.
For each property in each entity, the following attributes must be equal: name, isOptional, isTransient, isReadOnly, for attributes attributeType, and for relationships destinationEntity, minCount, maxCount, deleteRule, and inverseRelationship.
If your current xcdatamodel is a completely new file, I don't think that anything you do will make the original and the current version match. However, if the current version is simply the original one accidentaly modified, you can take a look at your older generated model files and try to figure out what changed.
E.g.: on the model you had a "NSString *age" property, but on your current xcdatamodel, the property is "NSNumber *age".
Also, remember that the error you are seeing can be caused by generated model files that are out of sync with the DB, so try to recreate them.
With a Core Data model file open (eg a blank one), choose Editor -> Import, and then find the .mom file from the previous version of your app.
See this answer.

Reverse-engineering Core Data db results in "Can't find model for source store" error

I have the task to re-engineer an iPhone app which makes use of Core Data to store some values. Unfortunately I do not have access to the original source code but I do have acces to old database files, copied directly from the device where the old version of the app is installed.
I have to create a new version of the app with some new functionality and I am trying to import the data from the Sqlite Db and migrate it to a new version.
I am already failing at the task to read the data from the old database. Though I can open the Sqlite file and such was able to exactly reproduce the data structure in my own datamodel, everytime I try to read the data, it fails with a
Can't find model for source store
error.
Ignoring the old data is not an option because there is important data stored there.
I googled for solutions and tried every recommended solution related to migrating data but it all fails. Maybe I can not use the Sqlite created by another app? Or so I overlook something in re-engineering the structure of the datamodel even when I used exactly the some field names and datatypes? Is there another way around this?
I could not solve the issue but I found a workaround. By using Sqlite directly, I was able to query the legacy data and import it into my newly created data model. A good starter point for using Sqlite is here:
http://www.techotopia.com/index.php/IOS_4_iPhone_Database_Implementation_using_SQLite

Resources