Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Periodic iCloud backup of SQLite database

Let me get this out of the way right now: yes, it was almost certainly a mistake to not use Core Data. However, I was new to iOS development when I made these decisions, and I had no idea I'd be hamstrung like this. Moreover, the app is intended to also run on Android (eventually), so I avoided platform-specific APIs wherever possible.

I have an iOS app that stores data in a local SQLite database file. The data stored in the file is provided by the user, so it's important that it be kept safe. I had plans to "do this later", and later is now here. I am quickly coming to the realization that it won't be as straightforward as I had hoped...

I now understand that it won't be possible to seamlessly synchronize data across devices, and I'm willing to accept that limitation until I manage to migrate to Core Data. However, in the meantime I'd at least like the SQLite database to be backed up periodically so users can feel safe using the app on a single device. I was thinking I would do this:

  • periodically (e.g. once a week) copy the SQLite file from local storage into cloud storage, thus ensuring it is backed up
  • when the app starts, if the local store is missing or corrupted but the file exists in the cloud storage, ask the user if they would like to copy it over

The biggest problem with this approach is that the user could run the app on multiple devices and therefore the data stored in iCloud could be from any one of those devices, but only one. To combat that, I thought I could just use a per-device, unique name for the file in cloud storage. I would generate this using UIDevice.identifierForVendor.

So my startup logic would be:

  1. Determine the unique name for the cloud file.
  2. Is the local file missing or corrupted, and if so, does the cloud file exist?

    2.1. Ask the user if they would like to restore from the cloud file. Make it really hard for them to say no because doing so will lose all their data.

    2.2. If they say yes, copy the cloud file to the local file storage.

  3. Open the local database file.

And running in the background I would occasionally copy the database file from local to cloud storage.

I would like to know whether this a sensible approach until I do Core Data integration. Also, are there any hidden "gotchas" that I'm perhaps missing?

UPDATE: as @TomHarrington pointed out in a comment, it turns out my database file is already sitting in /Documents, which is backed up to iTunes and any iCloud account. So my question morphs into this:

Should I simply ensure my database has a device-specific name so that it is not clobbered by the app running on another device connected to the same iCloud account?

like image 642
me-- Avatar asked Sep 02 '17 01:09

me--


People also ask

How do I automate iCloud backup?

To automatically back up your device each day, turn on iCloud Backup via Settings > [your name] > iCloud > iCloud Backup and toggle iCloud Backup to on. If you're using iOS 10.2 or earlier, go to Settings > iCloud > Backup. The device will then back up when your phone is connected to power, locked, and on Wi-Fi.

How you can take the backup of SQLite database?

Historically, backups (copies) of SQLite databases have been created using the following method: Establish a shared lock on the database file using the SQLite API (i.e. the shell tool). Copy the database file using an external tool (for example the unix 'cp' utility or the DOS 'copy' command).

Does Apple use SQLite?

SQLite is highly portable meaning it's compatible on virtually any platform from Windows to macOS to Linux to Android and naturally, iOS.


1 Answers

I'm going to answer my question, since I ended up going down this path and finding a MASSIVE blocker. There is a bug in the UIDevice.identifierForVendor API that causes it to regenerate every time a new version of the app is installed! See here. This of course rules out using it as a device identifier. sigh

I think I'm SOL with that approach. Instead, I might generate a GUID on first execution and use that as my identifier. Problem is, I need to store that somewhere that isn't backed up to iCloud.

Ugh, I may just give up here and say my app can't be run on multiple devices until Core Data integration is done.

UPDATE: I ended up generating an identifier on first run and storing it in the keychain (as a local entry only so it isn't backed up to iCloud).

like image 110
me-- Avatar answered Oct 13 '22 21:10

me--