メインコンテンツへスキップ

CapacitorにおけるKey-Valueストレージ:PreferencesとSQLiteの比較

Capacitor のキーバリューストレージを比較: Preferences、SQLite キーバリューストア、Keychain ストレージ。設定、キャッシュ、シークレットに最適な方法を知ろう。

Capgo のキーバリューストレージオプション比較のための Capacitor ロゴ

Capacitor でのキーバリューストレージには、 @capacitor/preferences 少数の小さな設定には Preferences、それより大容量・多数のデータには SQLite ベースのキーバリューストア、シークレットには Keychain または Keystore を使用します。本ガイドでは Capacitor 8 で利用可能なオプションを比較し、それぞれの実用的な code を示し、ユーザーデータを失わずにストレージを移行する方法を説明します。

多くのアプリでは、これらの中から2つを同時に使用します。アプリの状態管理には Preferences または SQLite、認証トークンにはセキュアストレージです。

選択肢の概要

localStorage / IndexedDB @capacitor/preferences 高速なSQL KeyValueStore @capgo/capacitor-data-storage-sqlite @capgo/capacitor-native-biometric setData
バックエンド: WebView ストレージ UserDefaults / SharedPreferences SQLiteファイル SQLiteファイル Keychain / Keystore
OSのストレージクリーンアップ後も残る 保証されない はい はい はい はい
値の型 文字列 (localStorage) 文字列 JSON値、数値、真偽値 Uint8Array 文字列 文字列
適切なサイズ範囲 小 小さい(値あたり数KB) 大きな値、多数のキー 多数のキー 非常に小さい(Androidでは約8KB未満)
暗号化 いいえ いいえ SQLCipherはオプション、キーはセキュアストレージから SQLCipherはオプション、パスフレーズは設定ファイルに記述 はい、ハードウェア保護
名前空間 オリジンごと group オプション store オプション テーブル キー名
プレフィックスでクエリ いいえ いいえ キーを一覧表示 filtervalues いいえ
Web はい localStorage SQLite Wasm (OPFS) IndexedDB シークレットには不向き

なぜ localStorage ではないのか

localStorage と IndexedDB は Capacitor の WebView 内で動作しますが、データは WebView に属し、アプリのネイティブストレージには属しません。デバイスの空き容量が不足すると、OS が WebView のストレージを回収する可能性があります。また、Capacitor のドキュメントでも、失ってはいけないデータに依存しないよう推奨されています。アプリのホスト名やスキームを変更すると capacitor.config オリジンも変わり、WebView は別の空のストレージを指すようになります。

これらは消えてもよいキャッシュとして使い、それ以外はネイティブストレージ対応のオプションを使ってください。

オプション 1: ちょっとした設定には Capacitor Preferences

@capacitor/preferences は軽量なキーバリューデータ用の公式プラグインです。iOSでは UserDefaultsに、Androidでは SharedPreferencesに、Webでは 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) });
}

用途: テーマ、言語、オンボーディングフラグ、最後に選択したタブ、機能トグル。

使用しないこと:

  • シークレット。 値はアプリサンドボックス内に平文で保存され、デバイスバックアップにも含まれます。
  • 大規模な値。 UserDefaults と SharedPreferences ファイル全体をメモリに読み込みます。大きなJSONブロブはすべての読み書きを遅くします。
  • プレフィックスで一覧表示やクリアが必要な多数のキー。 keys() すべてを返すため、JavaScriptでフィルタリングする必要があります。

Preferences.configure({ group: 'MyApp' }) キーが保存されるネームスペースを変更します。デフォルトのグループは CapacitorStorage.

オプション2: SQLiteキーバリューストア

キャッシュされた API レスポンス、下書き、オフラインキュー、または数百のエントリを保存する場合は、SQLiteファイルがより適しています。大きな値を処理でき、書き込みはアトミックであり、暗号化も可能です。

高速な SQL KeyValueStore

@capgo/capacitor-fast-sql には KeyValueStore SQLite接続上に構築されたクラスです。これにより、 __kv_store テーブルを作成し、値の型を保持し、1つのデータベース内で複数の名前付きストアをサポートします。

bun add @capgo/capacitor-fast-sql
bunx cap sync

Fast SQLには2つのネイティブ設定が必要です(iOSのローカルネットワーク設定と Info.plistAndroidのlocalhost cleartextルール)。これらは Capacitor アプリでSQLiteを使う方法.

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();

API の概要で説明しています: open(options), fromConnection(connection, store), set, get (存在しない場合は null 値を返します)、 has, remove, clear, keys, close.

知っておくべき2つのパターン:

複数ストア、1データベース。 この store オプションがキーを名前空間化するので、 settings, drafts と api-cache 並存でき、独立してクリアできます。

キーバリューと実テーブルの併用。 アプリがすでにリレーショナルデータに Fast SQL を使っている場合は、2 つ目の接続を開く代わりに既存の接続を再利用します:

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

後で、キーバリューのブロブがクエリしたい形に成長した場合 (例: 1 週間前の下書きすべて)、同じデータベース内の適切なテーブルに移行できます。

暗号化されたキーバリューストレージ

KeyValueStore.open と同じオプションを受け取ります FastSQL.connect、以下を含む encrypted と encryptionKey iOS と 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(),
});

ネイティブビルドに SQLCipher が必要です。セットアップ方法や、バックアップ復元後にキーを失った場合の対処法は、 Capacitor で SQLite データベースを暗号化する方法.

@capgo/capacitor-data-storage-sqlite

@capgo/capacitor-data-storage-sqlite SQLite 上で動作する専用のキーバリュープラグインです(Jean Pierre Quéau 氏が開発し、現在は Capgo がメンテナンスを担当)。値は文字列で、ストアごとに複数のテーブルを作成でき、プレフィックスやサフィックスによるフィルタリング、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 フィルタを SQL の LIKE パターンとしてキーに適用します(note% プレフィックスは %note 、サフィックスは、部分一致はプレーンテキスト)。パターンは SQL 文字列に直接挿入されるため、ユーザーからの生入力は渡さず、制御可能なフィルタのみを渡してください。

Web では IndexedDB を通じて localforage、これは別途インストールします:

bun add localforage

暗号化について: openStore({ encrypted: true, mode: 'secret' }) は、から取得したパスフレーズを使用して SQLCipher を利用します capacitor.config 内の plugins.CapgoCapacitorDataStorageSqlite.encryptionSecret。このパスフレーズはアプリのバンドル内に同梱されますが、設定しない場合、プラグインは組み込みのデフォルト値にフォールバックします。これはカジュアルなファイル検査に対する保護であり、アプリを解凍できる者に対するものではありません。本物のシークレットには、上記の Fast SQL の例のように、Keychain または Keystore のキーを使用してください。

Fast SQL KeyValueStore と data-storage-sqlite の違い

  • Pick 高速なSQL KeyValueStore data-storage-sqlite
  • Pick data-storage-sqlite data-storage-sqlite

オプション3: シークレット用のKeychainとKeystore

認証トークン、リフレッシュトークン、APIキー、データベース暗号化キーは、プラットフォームのセキュアストレージに保存すべきです。 @capgo/capacitor-native-biometric (8.6.0以降)は、iOS KeychainとAndroid Keystoreの上に汎用のキーバリューAPIを公開します:

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' });

値は小さく保ちましょう。プラグインのドキュメントでは、Android Keystoreベースの暗号化は1値あたり約8 KB以下で最も効果的とされています。大量のデータを保護する必要がある場合は、ここにランダムなキーを1つ保存し、それを使ってSQLiteデータベースを暗号化してください。私たちの記事 オフライントークンのためのセキュアストレージ では、トークン処理について詳しく解説しています。

iOSのKeychainアイテムは、アプリのアンインストール後も残存する可能性があります。クリーンな状態を想定しているアプリでは、新規インストール後の初回起動時に古いシークレットをクリアしてください(アンインストール時に削除されるPreferencesに「初回実行完了」フラグを保存し、それが存在しない場合はKeychainエントリを消去します)。

選択方法: 判断リスト

  1. シークレットですか? トークン、パスワード、PIN、キー。KeychainまたはKeystoreストレージを使用してください。
  2. 少量の小さな設定ですか? Preferencesを使用してください。
  3. 多数のエントリ、大きなJSON、バイナリデータ、またはグループ単位で期限切れにしてクリアしたいキャッシュはありますか? SQLiteのキーバリューストアを使用します。
  4. フィルタリング、ソート、または結合(JOIN)が必要ですか? もはやキーバリューデータではありません。テーブルを作成し、 SQLiteガイド、または次のような型付きレイヤーを使う: Kysely.

既存データのストア間移行

移行元 localStorage または Preferences から SQLite ストアへの移行は、起動時の一度きりのコピーです。アプリの他の部分がデータを読む前に実行し、実行済みであることを記録してください。

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;
}

機密情報をセキュアストレージにコピーしたら、すぐに古いストアから削除してください。Keychain に移した後も Preferences にトークンを残すと、元の露出がそのまま残ります。

この code は JavaScript で動作するため、一緒に配信できます。 Capgo live updateSQLite プラグイン自体はネイティブなので、これを追加するリリースにはストアビルドが必要です。まずプラグインを配信し、その後で移行用の code を配信してください。

トラブルシューティング

Preferences の値は null Web 上のアップデート後。 オリジン(ポート、ホスト名、スキーム)が変更されたため、 localStorage 別の空のストアになります。

高速なSQL KeyValueStore.open iOSでは例外をスローします。 ローカルHTTPチャネルがブロックされています。次を追加してください NSAllowsLocalNetworking まで Info.plist そしてネイティブアプリを再ビルドしてください。

get data-storage-sqliteでは、オブジェクトを期待する箇所で文字列が返されます。 そのプラグインは文字列のみを保存します。 JSON.parse 値。

新しい端末でバックアップを復元した後、シークレットが消失する問題。 Keystoreのキーやデバイス固有のKeychainアイテムは、新しいハードウェアには転送されません。セキュアストレージは、再ログインによって再取得可能な認証情報のキャッシュとして扱ってください。

iOSで再インストール後に設定が失われるが、トークンは残っている。 想定動作: Preferencesはアプリ削除時に消去されるが、Keychainの項目は残る可能性がある。上記の初回起動フラグパターンを使用すること。

まとめ

  • Preferences: 小規模で機密性のない設定。
  • SQLiteキーバリューストア (Fast SQL KeyValueStore または @capgo/capacitor-data-storage-sqlite): 多数または大規模な値、キャッシュ、下書き、任意で暗号化。
  • Keychain または Keystore (@capgo/capacitor-native-biometric setData): シークレットと暗号化キー。

プラグインのリファレンス: Fast SQL ドキュメント, data-storage-sqlite のドキュメント、および ネイティブ生体認証プラグインのページ.

Capacitor アプリ向けの Live Updates

Web レイヤーのバグが本番で発生したら、App Store の審査を何日も待たずに Capgo で修正を配信しましょう。ネイティブの変更は通常の審査フローに残したまま、ユーザーはバックグラウンドでアップデートを受け取れます。

最新のブログ記事

Capgoは、真にプロフェッショナルなモバイルアプリを作るために必要な最高のインサイトを提供します。