How long can core data migration take on startup? - ios

I have seen successful core-data migrations of my 4Gb database on my iPad on application launch taking several minutes.
And now suddenly, some users report crashes after installing a new version and the app is kicked out with a: failed to launch in time error.
I just tested again by restoring an old database and I am sure that core data migration can take way more than 10 seconds.
But other people are concerned it should not and try to take it to the background, or at least out of the run loop at launch time:
iPhone app launch times and Core Data migration
Can this have anything to to with other conditions, e.g. being connected to a power source? Or have a battery level of more than 50 %?
Update: I reproduced a crash by just starting the app on the device (unplugged) instead of debugging.
Then I tried starting the app on the device with USB attached: Crash.
Then started the app via the debugger: No crash (and the migration took about 4 minutes.)
Extra info: I have only enterprise users (about 75 of them) and they all have a database of 4.5Gb. Some users have no problem upgrading and some have. The upgrades all take minutes if they succeed. The crashes always come after 20 seconds. (And they keep crashing if you try again on these devices).
I followed the advice to place the migration out of the run loop, but I am still wondering why the old method works on some devices and not on others. All users are on iOS 7.

This is a common launch problem. A Core Data migration can take any amount of time, 0 to N depending on the complexity of the model and the amount of data and the type of migration occurring.
Ideally you should not be creating your Core Data stack in the -applicationDidFinish... method and migration is one of the reasons.
My recommendation is to rework your launch so that you display something until the stack has initialized. This could be just your default image in a view. Then when the Core Data stack has initialized you can switch over to your full view controller stack.
I would also recommend taking this a bit further so that you can tell the user that a migration is in process and I would further put the migration on a background queue so that you can update the UI while the migration is happening.
Lastly, if you are doing a heavy migration, I would look into doing a lightweight migration instead. The lightweight migration is far faster as well as other benefits.

If you look at the crash log it will likely say that the app was killed because it took too long to startup. The watchdog process kills apps that take too long to startup - >20 seconds I think. This is because the core data migration process was run during app startup.
I'd recommend you manually run the migration in the background. The following new book on Core Data has code and explanation for how to do a background manual migration.
http://www.amazon.com/gp/aw/d/0321905768

It is not a rule that don't run migration on background thread, but its a suggestion because if you run on background thread and your app start running, it is not guaranteed that your core data stack will not touch.
You can take this migration out of the didFinishLaunching but make sure that stack is not touch. You can handle this by some check like place a viewController with message that app is updating which don't allow user to do anything, and at mean time you can perfrom background migration. When migration process finish you can simply dismiss that viewController take user to home viewController.
When your app is running on the iOS platform you can not gurantee every thing, like some time if native apps need more memory then memory will cut off from your app quota and can get some wired kills.

Related

database becomes read-only/Corrupted

In our application we have been using an encrypted sqlite database in db3 format that is downloaded from server and then again after processing is uploaded. The app is live and is used by several users.
Sometimes, very intermittently in one or two instances, the database gets corrupted. The user has to discard the entire application and reinstall again to work resulting in data loss.
Only once we could detect that one of the tables got missing from the database through no drop table command was written anywhere in the code.
Did anyone face this instance before? Any idea why does this happens?
Please note: The application is iPad application written in objective C.
One of the main reason:
iDevices shut down quite a time before they'd actually run out of power. Before your device shuts down your App will get notified that it's going to background, and then get notified that it's going to quit. If you're handling those two notifications properly (i.e. closing all SQLite connections at one or the other) then you should not be getting database corruption.

Application failed to resume in time : Core Data

I am populating my core data with thousands of records in multiple entities one by one. This process is taking time to complete. This process is working fine when app is in active state. Once app goes in background and comes in foreground, the app is crashing with following error:
"failed to resume in time ios crash"
Please suggest some solution.
Thanks
The creating of the Core Data stack should never be done in the -applicationDidFinishLaunching If a migration is needed just inform the user about it but you need to return from the -applicationDidFinishLaunching... as fast as possible, you should only be creating UI elements in this method. You should not be accessing Core Data at this point.
You should not do very time-consuming tasks on the main thread. The crash is due to the populating process taking more than 10 seconds. It happened to me once in an infinite loop.
Start a new thread for this task, which will resume when your app enters foreground again. Tell the user what's going on, i.e. that the app is populating a database and that it may take some time. It is a good idea to show a progress indicator too.
As long as the user is kept well informed, he will accept to be patient.

Any way to debug app with dataset in production environment?

My CloudKit dataset in Production Environment is somewhat bigger than Development, and other exotic difference could exist.
There is a nasty deadlock using my app in Production Mode. Is it possible to debug client in any way? Or should I log as many thing as possible and send somehow out?
It is a threading issue, so without examining threads in Xcode it is really though to do anything. Any idea? I am using Core Data to local storage.
Rollback changes in the source code, to be able to run app.
Sync down records from Production Environment to local Core Data Storage.
Copy out in Xcode Device menu the sqlite database from container.
Create an temporary project with same model, populate it with the database.
Set up temporary project to able to use previous CloudKit container.
Reset Development Environment in Dashboard.
Upload all record from temporary project.
Run original project with original source code.
I would recommend using a crash reporting service. While there are a few options out there, I worked with Crashlytics, and I was very happy with the reports that they provided, always helping me to fix bugs in production.
When the app will go the the background, at some point it will be killed by iOS because your thread won't have answered to the -applicationDidEnterBackground, and then you will get a backtrace of all your threads.
If you want a better chance to trigger the kill (if the locked thread is not the main thread), you could grab a background task (- beginBackgroundTaskWithExpirationHandler:) in your working threads: if they are locked at some point they will never release the background task and they'll trigger the kill.
Now just wait for the iOS scheduler to kill your app and grab the stack trace. In there, you should be able to find the culprit by looking at all you thread's backtraces and identify which ones are locked in a mutex lock() function.
I bet you don't even need symbolication for that.

Is MobileServiceSQLiteStore.DefineTable<T> necessary on every run and if so why?

I'm trying to improve app launch performance for subsequent logins (every login after the first) with my mobile app and after putting some stop watch diagnostics I can see that defining my 8 tables with MobileServiceSQLiteStore.DefineTable<T> takes on average 2.5 seconds. Every time.
On an iPhone 4 running iOS 7 the loading time would be less than a second if it weren't for having to define these tables every time. I would expect them to only need to be defined the first run of the app when the SQLite database is setup. I've tried removing the definitions on subsequent logins and try to just get the sync tables but it fails with "Table is not defined".
So, it seems this is the intended behavior. Can you explain why they need to be defined each time and/or if there is any workaround for this? It could be negligible considering my phone is pretty old now.. but it still is something I would like to remove if possible.
Yes, it is required to be called every time because SDK uses it to know how to deserialize data if you read it via untyped interface i.e. IMobileServiceSyncTable instead of IMobileServiceSyncTable<T>.
As of now there is no work around to avoid calling it each time. I'm surprised however that it is taking 2.5 seconds for you because DefineTable does not do any database operations. It merely inspects the members on your type/JObject and maintains an in memory dictionary for later re-use.
I would recommend you to download and compile the SDK and debug your way through to figure out where the time is actually spent.

Data vanishing in Core Data

I'm getting crash reports from a large percentage of my user base, and have tracked it down to a core data entity having every record deleted somehow.
This entity is one where the app downloads every row the first time you launch the app, and then the table is never written to again, it's just a read only lookup table.
It shouldn't be possible for anything to ever be removed from the entity, but that's what I'm seeing. All reports are the app functions fine for a while (sometimes days, other times minutes after a fresh install) and then suddenly starts crashing any time you try to do anything.
I've gone over the entire code base and nothing ever deletes data from the entity. The bug only occurs in a version of the app just released. Rolling all users back to the previous release - only two weeks old, has fixed the bug for for everyone. It's an enterprise app, I simply redeployed the old binary.
I went over every change in source control and found nothing related to the entity.
In fact all code related to this entity has not changed at all in the last few years. It's been perfectly stable all that time.
Other data is persisted to disk in the same -[NSManagedObjectContext save:] call, and that data does not vanish.
I'm stumped. How can data in an entity be erased? How can I debug this issue further?
I am unable to reproduce the bug on my own devices, although I have extensive logging in place (and am happy to add more — this is an enterprise app only ever used for work so user privacy isn't an issue).

Resources