Working on iOS I encountered a really interesting issue, well the issue by itself is pretty annoying and hard to debug however when you understand why it’s happening you may find it interesting. Here what happened:
I got an application which is always running in order to collect locations and other data sensors. If the app is in foreground everything works fine, when it is in background and the device is not locked everything works fine, when the device is locked without passcode everything works fine, when the device is locked with passcode everything works fine.. the first ~10 seconds. Then one of the following* exception shows up:
SQLite.SQLiteException: IOError SQLite.SQLiteException: AccessDenied SQLite.SQLiteException: CannotOpen
*I made several tries before finding a solution, different exception comes from different tries.
a little a lot I found out the interesting thing. iOS passcode is used to actually encrypt datas on your phone! With this surprising (at least to me) revelation why such problem happens became pretty clear. SQLite need to access the filesystem which is encrypted after the device is locked; the ~10 seconds is the time that the device required to encrypt and to do whatever it has to do.
Actually is a little more complicated. There are different protection level, each file can has its NSFileProtection attribute with defines when it has to be encrypted/decrypted:
NSFileProtectionNone NSFileProtectionComplete NSFileProtectionCompleteUnlessOpen NSFileProtectionCompleteUntilFirstUserAuthentication
Once find out why it happens, the solution looks pretty trivial. Set the database with NSFileProtectionNone attribute. Well it’s not so easy.
First I tried to manually set it on the .db file, doesn’t work, just changes exception.. probably because there also other file used by SQLite (Journaling?)
Then I tried with open flags and I found I way to make it works. The following code rely on the fact that I use Xamarin.iOS with SQLite.net, but I think the solution should be quite similar everywhere:
new SQLiteConnection (SqlitePlatform, DatabasePath, SQLiteOpenFlags.ProtectionNone | SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create);
Actually googling I found some feedback of people that using the same flags has different result (still crash), anyway for me it works, so give it a try.
Moreover that code was shared also on an Android project, and it works as before probably ignoring the iOS flags.