4.2 KiB
What to do about storage for native apps?
Problem
We can't trust iOS IndexedDB to persist. I want to start delivering an app to people now, in preparation for presentations mid-June: Rotary on June 12 and Porcfest on June 17.
-
Apple WebKit puts a 7-day cap on IndexedDB.
- The web standards expose a
persist
method to mark memory as persistent, and supposedly WebView supports it, but too many other things indicate it's not reliable. I've talked with ChatGPT & Venice & Claude (in Cursor); this answer from Perplexity says that most platforms don't prompt and Safari doesn't support it; I don't know if that means WebKit as well.
- The web standards expose a
-
Capacitor says not to trust it on iOS.
Also, with sensitive data, the accounts info should be encrypted.
Options
-
There is a community SQLite plugin for Capacitor with encryption by SQLCipher.
- This tutorial shows how that plugin works for web as well as native.
-
Capacitor abstracts user preferences in an API, which uses different underlying libraries on iOS & Android. Unfortunately, it won't do any filtering or searching, and is only meant for small amounts of data. (It could be used for settings and for identifiers, but contacts will grow and image blobs won't work.)
- There are hints that Capacitor offers another custom storage API but all I could find was that Preferences API.
-
Ionic Storage is an enterprise solution, which also supports encryption.
-
Not an option yet: Dexie may support SQLite in a future version.
Current Plan
-
Implement SQLite for Capacitor & web, with encryption. That will allow us to test quickly and keep the same interface for native & web, but we don't deal with migrations for current web users.
-
After that is delivered, write a migration for current web users from IndexedDB to SQLite.
Current method calls
... which is not 100% complete because the AI that generated thus claimed no usage of 'temp' DB.
Secret Database (secretDB) - Used for storing the encryption key
secretDB.open() - Opens the database secretDB.secret.get(MASTER_SECRET_KEY) - Retrieves the secret key secretDB.secret.add({ id: MASTER_SECRET_KEY, secret }) - Adds a new secret key
Accounts Database (accountsDB) - Used for storing sensitive account information
accountsDB.open() - Opens the database accountsDB.accounts.count() - Counts number of accounts accountsDB.accounts.toArray() - Gets all accounts accountsDB.accounts.where("did").equals(did).first() - Gets a specific account by DID accountsDB.accounts.add(account) - Adds a new account
Non-sensitive Database (db) - Used for settings, contacts, logs, and temp data
Settings operations: export all settings (Dexie format) db.settings.get(MASTER_SETTINGS_KEY) - Gets default settings db.settings.where("accountDid").equals(did).first() - Gets account-specific settings db.settings.where("accountDid").equals(did).modify(settingsChanges) - Updates account settings db.settings.add(settingsChanges) - Adds new settings db.settings.count() - Counts number of settings db.settings.update(key, changes) - Updates settings
Contacts operations: export all contacts (Dexie format) db.contacts.toArray() - Gets all contacts db.contacts.add(contact) - Adds a new contact db.contacts.update(did, contactData) - Updates a contact db.contacts.delete(did) - Deletes a contact db.contacts.where("did").equals(did).first() - Gets a specific contact by DID
Logs operations: db.logs.get(todayKey) - Gets logs for a specific day db.logs.update(todayKey, { message: fullMessage }) - Updates logs db.logs.clear() - Clears all logs