Best Way To Fix Core Data Migration Error - ios

I have an application which uses core data and I have set up lightweight migration. When you first create the app xcode generates default methods. The persistentStoreCoordinator getter calls the abort() method and it says it should be replaced in a shipping application. I think the best way for my app to handle this failed migration would be to completely reset core data and then reload all the info. Is this the best method and if so how would I go about doing this?

The "best" answer always depends on your application.
This error should only ever happen during development. With proper testing, you should never see this error in production. I always consider it a development level error and therefore a NSLog and abort() are appropriate.
If your data is replaceable (i.e. you are using CD to cache data from the internet, etc.) then yes it makes sense to delete the database on this error and rebuild it from scratch. If your data is not replaceable then this is a terrible solution, see point 1.
Update 1
Putting this in an update because it is important and I don't want it getting lost in the comments:
#MarcusS.Zarra I know that this error should never happen, however I don't think it is unrecoverable.
In production, if you error on -addPersistentStore... it is unrecoverable as far as that database otherwise you would not have hit the error in the first place. Hence the two suggestions above, test more or replace the data.
There are two errors (ignoring iCloud) that you will get from that method, failed migration or failed to open the file.
If you failed to open the file the file is dead and is not going to be opened without human intervention.
If you failed the migration then you are missing the source data model and you are not going to recover it without a new build of the application that includes the source model.
Having a bad data file is extraordinarily rare. It indicates a fault with the file system or some other extremely unusual circumstance.
Not having the source data model is a 100% developer avoidable situation.
In either case, in production, you are not recovering from the error.

For development purposes I've previously added code to delete the database file before abort() is called.. That way when you relaunch the app, the database is reconfigured automatically to save you having to manually delete the app

Related

Definitive list of possible reasons NSManagedObjectContext save might fail

TL;DR version: is there a definitive list of possible reasons that -[NSManagedObjectContext save:] could fail? I'm looking in CoreDataErrors.h, but it's not clear which of the keys relate to NSDetailedErrorsKey.
Background: I'm maintaining a shipping app written in a combination of Swift and Objective-C. It uses JSQCoreDataKit. Someone else wrote the code originally.
We have a production crash that we have not been able to reproduce. We can tell from the stacktrace that it is crashing in an error handler following an NSManagedObjectContext save failure with EXC_BAD_ACCESS trying to log the NSError to the console. The save failure is on a child context and we know the model class which was being modified at the time.
Hence we can tell that the save is failing, but we have no detailed information about the reason for the save failure.
We want to work backwards from the reason for the save failure, in the hopes of working out a reproduction for the crash, in order to test any potential fixes.
The crash is actually a side effect of the save failure. So although we want to fix the crash, we also need to work out the reason for the save failure.
For 95% of users, the save happens without problems.
The model has some non-optional fields and the data comes from JSON parsed from a server response. At this stage we have no reason to suspect the server is sending us bad data.
Does anyone know of a definitive list of possible reasons for a save failure that we could work through, eliminating options?
So far, I'm aware of:
validation failure e.g. a missing a required value, or a value outside specified max/min values in the model.
a possible save conflict (see NSPersistentStoreSaveConflictsErrorKey). But it's not clear if it's possible to have this when saving from a child context to a parent context.
There should never be a failure -save: with no error. Every time I have seen that situation it was my fault in my code somewhere.
Can you update the question with showing the code around the error location?
The possible failures of a save are:
Merge failure (most common)
Validation failure (less common)
No store defined in the NSPersistentStore
A nil NSManagedObjectContext (this would present the case you describe)
All of those except for the last will produce an error object to interrogate.

Database encrypted by SQLCipher in an iOS app is becoming permanently inaccessible

I recently modified my iOS app to enable serialized mode for both a database encrypted using SQLCipher and a non-encrypted database (also SQLite). I also maintain a static sqlite3 connection for each database, and each is only opened once (by simply checking for null values) and shared throughout the lifetime of the app.
The app is required to have a sync-like behavior which will download a ton of records from a remote database at regular intervals using a soap request and update the contents of the local encrypted database. Of course, the person using the app may or may not be updating or reading from the database, depending on what they're doing, so I made the changes mentioned in the above paragraph.
When doing short term testing, there doesn't appear to be any issue with how things work, and I've yet experience any problem.
However, some users are reporting that they've lost access to the encrypted database, and I'm trying to figure out why.
My thoughts are as follows: Methods written by another developer declared all sqlite3_stmt's to be static (I believe this code was in the problematic release). In the past I've noticed crashes when two threads using a particular method run simultaneously. One thread finalizes, modifies or replaces a sqlite3_stmt while another thread is using it. A crash doesn't always occur because he has wrapped most of his SQLite code in try/catch blocks. If it's true that SQLite uses prepare and finalize to implement locking, could the orphaning of sqlite3_stmt's which occurs due to their static nature in this context be putting the database into an inoperable state? For example, when a statement acquires an exclusive lock after being stepped is replaced by an assignment in the same method running in another thread?
I realize that this doesn't necessarily mean that the database will become permanently unusable, but, consider this scenario:
At some point during the app's lifetime it will re-key the encrypted database and that key is stored in another database. Suppose that it successfully re-keys the encrypted database, but then the new key is not stored in the other database because of what I mentioned above.
Provided that the database hasn't become corrupted at some point (I'm not really counting on this being the case), this is the only explanation I can come up with for why the user may not be able to use the encrypted database after restarting the iOS app, seeing as the app would be the only one to access the database file.
Being that I can't recreate this issue, I can only speculate about what the reasoning might be. What thoughts do you have? Does this seem like a plausible scenario for something that happens rarely? Do you have another idea of something to look into?
If the database is rekeyed, and the key for the database is not successfully stored in the other database, then it could certainly cause the problem.

What should I do after crashing when writing into a sqlite db (iOS)?

If an app crashes when writing into a sqlite db (or CoreData), sometimes the db file will be broken, after which initialisation of the db may fail to open.
What I'm doing now is deleting the db file if it fails to open, and copying a new one to be used.
I'm wondering what's the BEST WAY to deal with such situation?
Due to the atomic commit nature of SQLite, you should never experience database corruption, if you are, it could be due to enabling features such as "Write Caching" within iOS or in the hard drive itself, or could possibly even be caused by hardware failure.
SQLite maintains a journal file to rollback commits and return the database to a consistent state in the event of a power failure or other abrupt shutdown. If corruption occurs, it means that the OS responded to SQLite stating a write operation had completed when in actuality, it wasn't physically committed to the media yet. Please ensure Write Caching is disabled when using it in your App. For more information, please see the SQLite Atomic Commit reference.
Otherwise the common method people seem to "repair" a SQlite DB is to .dump the DB file into another. Like so echo ".dump" | sqlite old.db | sqlite new.db
Hope this helps...
[source]

Unit Testing with Kiwi, Core Data and Magical Record

I'm having issues using a 'fake' store for my Unit Tests.
I have installed Kiwi by adding its framework folder to my project and replacing the Xcode's default test cases with Kiwi tests. These all run fine.
Since I'm using Core Data, I need to create a 'fake' store so I'm playing with the real database. I used http://www.cimgf.com/2012/05/15/unit-testing-with-core-data/ as my basic guide to do this.
However, since Xcode's default test implementation runs tests after launching the app, my '[MagicalRecord setupCoreDataStackWithStoreNamed:#"Store.sqlite"]' is still fired inside the App Delegate before any of the tests run.
By the time the tests try to use '[MagicalRecord setupCoreDataStackWithInMemoryStore]', this sqlite store is set up, and so the in-memory store doesn't get set up (AFAIK), since the aforementioned setup stack method checks first to see if a stack already exists, and just returns without executing anything if it does, so I end up with the sqlite database still.
As far as I can tell, this leaves me with the following options:
Put some environment variables or flags in for the test cases, and check for these in the app delegate, creating the appropriate store depending on this variable (i.e. tweaking my actual code for the sake of testing - not pretty, nor recommended by any practising TDD/BDDers).
Add managed context properties on all my controllers so I can manually specify the store to use (removing a great deal of the niceties of the MagicalRecord singleton access pattern).
Play (carefully) with my actual database (I'm not really willing to even contemplate this).
None of these seems to be a particularly good solution, so I'm hoping someone can see a better solution that I've stupidly overlooked.
Your tests should not be launching the app delegate. Try setting up your tests so that only the tests setup the in-memory core data store, as suggested in the article you reference.

CoreData between app updates, signal a default-data refresh

when dealing with CoreData, I've run into a few problems I'm trying to nip in the bud for future proofing the system out of the gate. The simple fact of the matter is that I've never done anything like this before (work with CoreData that is). While I've managed to figure out how to work with it in the app, I need to know a decent practice to signal an app between versions that default data needs to be refresh on first app launch.
So right now, in my AppDelegate, I setup my managed object context, and I perform a fetch request to see if there are any records at all in a particular table/entity. I only want this to happen on first launch so im not constantly rewriting the contents of the DB every app launch. Anyways, so it goes ahead and uses Object Models to handle inserting of data amongst the entities in question (theres a few)
Now, for this version of the app, it's going into the store without an API (thats a far future thing), but between versions released to the app store, we may have to update specific information within the entities (for example: prices), again I only want this refresh to happen on app launch. Also, the schema MIGHT change, Im not sure if or when, but I'd like to make sure this can accomodate that just in case.
I figured, versioning the coredata "Add Model Version" would do the trick, set the new db version as the active version, but when I launch the app in the simulator, nothing happens which tells me that the data inside is being retained.
Any help towards what it is that I should do to accomodate this would be appreciated. Thank you!
You should find the Core Data Model Versioning and Data Migration guide useful:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html
You'll also probably find Method for import initial data with coredata useful.

Resources