Per lo storage key-value in Capacitor, usa @capacitor/preferences per un piccolo numero di impostazioni, uno store key-value basato su SQLite per dati più grandi o numerosi, e Keychain o Keystore per i segreti. Questa guida confronta le opzioni disponibili su Capacitor 8, mostra code funzionanti per ciascuna e spiega come passare da una all'altra senza perdere i dati degli utenti.
La maggior parte delle app usa due di queste opzioni contemporaneamente: Preferences o SQLite per lo stato dell'app, e storage sicuro per il token di autenticazione.
Panoramica delle opzioni
localStorage / IndexedDB |
@capacitor/preferences |
SQL veloce KeyValueStore |
@capgo/capacitor-data-storage-sqlite |
@capgo/capacitor-native-biometric setData |
|
|---|---|---|---|---|---|
| Basato su | Archiviazione WebView | UserDefaults / SharedPreferences | File SQLite | File SQLite | Keychain / Keystore |
| Sopravvive alla pulizia dell'archiviazione OS | Non garantito | Sì | Sì | Sì | Sì |
| Tipi di valore | Stringhe (localStorage) | Stringhe | Valori JSON, numeri, booleani, Uint8Array |
Stringhe | Stringhe |
| Buon range di dimensioni | Piccolo | Piccolo, pochi KB per valore | Valori grandi, molte chiavi | Molte chiavi | Molto piccolo (meno di ~8 KB su Android) |
| Crittografia | No | No | SQLCipher facoltativo, chiave da storage sicuro | SQLCipher facoltativo, passphrase nella config | Sì, hardware-backed |
| Namespaces | Per origine | group opzione |
store opzione |
Tabelle | Nomi chiave |
| Query per prefisso | No | No | Elenca le chiavi | filtervalues |
No |
| Web | Sì | localStorage |
SQLite Wasm (OPFS) | IndexedDB | Non per segreti |
Perché non usare localStorage
localStorage e IndexedDB funzionano all'interno di una WebView Capacitor, ma i dati appartengono alla WebView, non allo storage nativo della tua app. Il sistema operativo può recuperare lo storage della WebView quando lo spazio sul dispositivo è scarso, e la documentazione di Capacitor stessa sconsiglia di fare affidamento su di essa per dati che non puoi perdere. Cambiando l'hostname o lo scheme dell'app in capacitor.config cambi anche l'origine, che punta la WebView a uno storage diverso e vuoto.
Usali come cache che può scomparire. Usa una delle opzioni basate su storage nativo per tutto il resto.
Opzione 1: Capacitor Preferences per piccole impostazioni
@capacitor/preferences è il plugin ufficiale per dati key-value leggeri. Su iOS scrive in UserDefaults, su Android in SharedPreferences, sul web in localStorage.
bun add @capacitor/preferences
bunx cap sync
import { Preferences } from '@capacitor/preferences';
interface Settings {
theme: 'light' | 'dark' | 'system';
fontScale: number;
}
const DEFAULTS: Settings = { theme: 'system', fontScale: 1 };
export async function loadSettings(): Promise<Settings> {
const { value } = await Preferences.get({ key: 'settings' });
return value ? { ...DEFAULTS, ...JSON.parse(value) } : DEFAULTS;
}
export async function saveSettings(settings: Settings): Promise<void> {
await Preferences.set({ key: 'settings', value: JSON.stringify(settings) });
}
Usalo per: tema, lingua, flag di onboarding, ultima tab selezionata, feature toggles.
Non usarlo per:
- Segreti. I valori sono salvati in chiaro nel sandbox dell'app e inclusi nei backup del dispositivo.
- Valori di grandi dimensioni.
UserDefaultseSharedPreferencescaricano l'intero file in memoria. I grandi blob JSON rallentano ogni lettura e scrittura. - Molte chiavi da elencare o cancellare per prefisso.
keys()restituisce tutto, quindi devi filtrare in JavaScript.
Preferences.configure({ group: 'MyApp' }) cambia il namespace sotto cui sono salvate le chiavi. Il gruppo predefinito è CapacitorStorage.
Opzione 2: un key-value store SQLite
Quando memorizzi risposte API in cache, bozze, code offline o centinaia di voci, un file SQLite è la scelta migliore. Gestisce valori grandi, le scritture sono atomiche e puoi criptarlo.
KeyValueStore SQL veloce
@capgo/capacitor-fast-sql include un KeyValueStore classe basata sulla sua connessione SQLite. Crea una __kv_store tabella, mantiene il tipo di valore e supporta più store con nome in un unico database.
bun add @capgo/capacitor-fast-sql
bunx cap sync
Fast SQL richiede due impostazioni native (networking locale su iOS in Info.plist, una regola cleartext per localhost su Android). Sono descritte in Come usare SQLite in un'app Capacitor.
import { KeyValueStore } from '@capgo/capacitor-fast-sql';
const cache = await KeyValueStore.open({ database: 'app', store: 'api-cache' });
// Objects, arrays, numbers, booleans, null and Uint8Array are stored as-is
await cache.set('user:42', { id: 42, name: 'Ada', roles: ['admin'] });
await cache.set('lastSync', Date.now());
const user = (await cache.get('user:42')) as { id: number; name: string } | null;
const exists = await cache.has('lastSync');
const allKeys = await cache.keys();
await cache.remove('user:42');
await cache.clear(); // only clears the 'api-cache' store
await cache.close();
Riepilogo di API: open(options), fromConnection(connection, store), set, get (restituisce null quando mancano), has, remove, clear, keys, close.
Due pattern da conoscere:
Più store, un unico database. Il store l'opzione suddivide le chiavi in namespace, quindi settings, drafts e api-cache possono coesistere ed essere svuotati indipendentemente.
Key-value accanto a tabelle reali. Se la tua app usa già Fast SQL per i dati relazionali, riutilizza la connessione invece di aprirne una seconda:
import { FastSQL, KeyValueStore } from '@capgo/capacitor-fast-sql';
const db = await FastSQL.connect({ database: 'app' });
const drafts = await KeyValueStore.fromConnection(db, 'drafts');
// You own `db`: drafts.close() does not disconnect it
In seguito, quando un blob key-value diventa qualcosa che vuoi interrogare (ad esempio tutte le bozze più vecchie di una settimana), puoi spostarlo in una tabella vera e propria nello stesso database.
Storage key-value criptato
KeyValueStore.open accetta le stesse opzioni di FastSQL.connect, tra cui encrypted e encryptionKey su iOS e Android:
import { KeyValueStore } from '@capgo/capacitor-fast-sql';
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
async function getKey(): Promise<string> {
const { isSaved } = await NativeBiometric.isDataSaved({ key: 'kv.key' });
if (isSaved) return (await NativeBiometric.getData({ key: 'kv.key' })).value;
const bytes = crypto.getRandomValues(new Uint8Array(32));
const key = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
await NativeBiometric.setData({ key: 'kv.key', value: key });
return key;
}
const secureStore = await KeyValueStore.open({
database: 'secure-kv',
encrypted: true,
encryptionKey: await getKey(),
});
Questo richiede SQLCipher nelle build native. La configurazione, insieme a cosa fare se la chiave viene persa dopo il ripristino di un backup, si trova in Come crittografare un database SQLite in Capacitor.
@capgo/capacitor-data-storage-sqlite
@capgo/capacitor-data-storage-sqlite è un plugin key-value dedicato basato su SQLite (originariamente di Jean Pierre Quéau, ora mantenuto da Capgo). I valori sono stringhe, e aggiunge alcune funzionalità extra: più tabelle per store, filtraggio per prefisso e suffisso, e import/export JSON.
bun add @capgo/capacitor-data-storage-sqlite
bunx cap sync
import { CapgoCapacitorDataStorageSqlite as Store } from '@capgo/capacitor-data-storage-sqlite';
await Store.openStore({ database: 'appStore', table: 'settings' });
await Store.set({ key: 'locale', value: 'fr' });
await Store.set({ key: 'profile', value: JSON.stringify({ name: 'Ada' }) });
const { value } = await Store.get({ key: 'locale' });
const { result: hasProfile } = await Store.iskey({ key: 'profile' });
const { keys } = await Store.keys();
// Switch table inside the same store
await Store.setTable({ table: 'drafts' });
const { values } = await Store.filtervalues({ filter: 'note%' }); // values whose key starts with "note"
await Store.remove({ key: 'note-1' });
await Store.clear(); // clears the current table
filtervalues applica il filtro come SQL LIKE pattern sulla chiave (note% per un prefisso, %note per un suffisso, testo semplice per contains). Il pattern viene inserito nella stringa SQL, quindi passa solo filtri che controlli tu, mai input grezzi dell'utente.
Sul web usa IndexedDB tramite localforage, che installi separatamente:
bun add localforage
Sulla sua crittografia: openStore({ encrypted: true, mode: 'secret' }) usa SQLCipher con una passphrase da capacitor.config sotto plugins.CapgoCapacitorDataStorageSqlite.encryptionSecret. Quella passphrase è inclusa nel tuo bundle app e, se non la imposti, il plugin usa un valore predefinito interno. Considerala una protezione contro ispezioni casuali dei file, non contro chi smonta la tua app. Per segreti reali usa una chiave da Keychain o Keystore, come nell'esempio Fast SQL sopra.
KeyValueStore SQL veloce o data-storage-sqlite?
- Scegli SQL veloce
KeyValueStorese vuoi valori tipizzati senza JSON manuale, valori binari, una chiave di crittografia runtime o usi già Fast SQL per le tabelle. - Scegli data-storage-sqlite se ti servono le sue funzioni di tabella e filtri, l'export e l'import JSON, o il supporto per Electron.
Opzione 3: Keychain e Keystore per i segreti
Gli auth token, i refresh token, le chiavi API e le chiavi di crittografia del database vanno salvati nello storage sicuro della piattaforma. @capgo/capacitor-native-biometric (8.6.0 e versioni successive) espone una generica API key-value sopra iOS Keychain e Android Keystore:
bun add @capgo/capacitor-native-biometric
bunx cap sync
import { AccessControl, NativeBiometric } from '@capgo/capacitor-native-biometric';
// Silent read and write
await NativeBiometric.setData({ key: 'session.refreshToken', value: token });
const { value: refreshToken } = await NativeBiometric.getData({ key: 'session.refreshToken' });
// Require Face ID / Touch ID / fingerprint to read
await NativeBiometric.setData({
key: 'wallet.pin',
value: pin,
accessControl: AccessControl.BIOMETRY_ANY,
});
const { value: secretPin } = await NativeBiometric.getSecureData({
key: 'wallet.pin',
reason: 'Confirm it is you',
});
await NativeBiometric.deleteData({ key: 'session.refreshToken' });
Mantieni i valori piccoli. Il plugin documenta che la crittografia basata su Android Keystore funziona al meglio sotto gli 8 KB circa per valore. Se devi proteggere molti dati, salva qui una chiave casuale e usala per crittografare un database SQLite. Il nostro post su lo storage sicuro per token offline approfondisce la gestione dei token.
Ricorda che gli elementi Keychain su iOS possono sopravvivere alla disinstallazione dell'app. Al primo avvio dopo un'installazione pulita, elimina i segreti obsoleti se la tua app si aspetta uno stato iniziale pulito (salva un flag "first run done" in Preferences, che viene rimosso alla disinstallazione, e cancella le voci Keychain quando manca).
Come scegliere: una lista di decisioni
- È un segreto? Token, password, PIN, chiave. Usa lo storage Keychain o Keystore.
- Sono poche piccole impostazioni? Usa Preferences.
- Sono molte voci, JSON grandi, dati binari o cache da far scadere e cancellare in gruppi? Usa uno store key-value SQLite.
- Devi filtrare, ordinare o fare join? Non è più un dato key-value. Crea una tabella, consulta la guida SQLite, oppure usa un layer tipizzato come Kysely.
Migrare i dati esistenti tra store diversi
Passare da localStorage o Preferences a un store SQLite è una copia una tantum all'avvio. Fallo prima che il resto dell'app legga i dati e registra che è stato eseguito.
import { Preferences } from '@capacitor/preferences';
import { KeyValueStore, type KeyValueValue } from '@capgo/capacitor-fast-sql';
export async function migrateToSqliteStore(): Promise<KeyValueStore> {
const kv = await KeyValueStore.open({ database: 'app', store: 'settings' });
if (await kv.has('__migrated_v1')) return kv;
const { keys } = await Preferences.keys();
for (const key of keys) {
const { value } = await Preferences.get({ key });
if (value === null) continue;
let parsed: KeyValueValue = value;
try {
parsed = JSON.parse(value) as KeyValueValue;
} catch {
// keep plain strings as strings
}
await kv.set(key, parsed);
}
// Also copy from localStorage if older versions used it
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && !(await kv.has(key))) {
await kv.set(key, localStorage.getItem(key));
}
}
await kv.set('__migrated_v1', true);
// Remove the old copies only after a release or two, once you are confident
return kv;
}
Rimuovi i segreti dal vecchio store non appena vengono copiati nello storage sicuro. Lasciare un token in Preferences dopo averlo spostato nel Keychain mantiene l'esposizione originale.
Poiché questo code viene eseguito in JavaScript, puoi distribuirlo con un Capgo live update. Il plugin SQLite stesso è nativo, quindi la release che lo aggiunge richiede una build per lo store. Distribuisci prima il plugin, poi la migrazione code.
Risoluzione problemi
I valori di Preferences sono null dopo un aggiornamento sul web. L'origine è cambiata (porta, hostname o schema), quindi localStorage è uno store diverso e vuoto.
SQL veloce KeyValueStore.open genera un errore su iOS. Il canale HTTP locale è bloccato. Aggiungi NSAllowsLocalNetworking a Info.plist e ricostruisci l'app nativa.
get restituisce una stringa dove in data-storage-sqlite ti aspettavi un oggetto. Quel plugin memorizza solo stringhe. JSON.parse il valore.
Secret mancanti dopo il ripristino di un backup su un nuovo telefono. Le chiavi Keystore e gli elementi Keychain specifici del dispositivo non vengono trasferiti sul nuovo hardware. Considera lo storage sicuro come una cache di credenziali che puoi recuperare effettuando nuovamente il login.
Impostazioni perse dopo la reinstallazione, ma i token sono ancora lì su iOS. Atteso: Preferences viene eliminato con l'app, gli elementi Keychain possono rimanere. Usa il pattern della flag di prima esecuzione sopra.
Riassunto
- Preferences: impostazioni piccole e non sensibili.
- Key-value store SQLite (Fast SQL
KeyValueStoreoppure@capgo/capacitor-data-storage-sqlite): molti o grandi valori, cache, bozze, opzionalmente cifrati. - Keychain o Keystore (
@capgo/capacitor-native-biometricsetData): segreti e chiavi di cifratura.
Riferimenti ai plugin: Docs Fast SQL, documentazione data-storage-sqlite, e il pagina del plugin biometrico nativo.