💡

iOSでexpo-secure-storeに保存したデータがアンインストール後も消えない問題

2024/10/03に公開

はじめまして!ファンタラクティブのyanoです。

社内プロジェクトではじめてモバイルアプリ開発に挑戦中なので、発生した問題や取り組みを備忘録として残していきます。

はじめに

タイトルに記載のとおりですが、
「プラットフォームにおけるデータ保持の仕組みを理解していないと気づかない&Androidと挙動が異なる」という点で意外と気づきにくいです。

きっかけ

アプリ内でストレージ情報に基づいてスクリーンを出し分けするという処理があるのですが、再インストール後にiOSだけスクリーン表示がリセットされていないということが発覚したためです。

やりたいこと

Androidの挙動に合わせたいです。つまり、アプリ初回起動時にストレージ情報がリセットされていてほしいということになります。

expo-secure-storeとは

https://docs.expo.dev/versions/latest/sdk/securestore/
「キーと値のペアをデバイス上でローカルに暗号化して安全に保存する方法を提供するライブラリ」と記載されてます。各プラットフォームが用意している以下のセキュアな保管手段を利用しているようです。

  • iOS: keychain service
  • Android: Secure Shared Prefernces

「内部でKeychainを使っている」という点が、今回のキーワードです。

なぜiOSではアンインストールしてもデータが消えないのか

iOSにおいては内部でKeychainを使っているので消えないとのことです。
https://qiita.com/sachiko-kame/items/261d42c57207e4b7002a
「expo-secure-store」という名前から迷わず選定しましたが、内部の仕組みを理解しておくことは重要です。

Androidの挙動に合わせる

https://stackoverflow.com/questions/4747404/delete-keychain-items-when-an-app-is-uninstalled
AsyncStorage(アプリの設定を管理できる)を組み合わせて、Androidの挙動に近づける事で解決します。厳密には、ストレージ削除タイミングが異なります。(Androidではアンインストール直後に削除されますが、iOSではアンインストール直後の処理を管理できないので、初回起動タイミングを判定して起動直後に削除されるようにします)

つまり、iOSではアプリが初回起動(再インストール含む)か否かを「AsyncStorage」で判定して、初回起動だったら「expo-secure-store(Keychain)」のストレージ情報を削除します。

※ 一度アンインストールしたら、「AsyncStorage」の中身が消えるので、再インストール後は初回起動として扱うことができます。

以下のように実装しました。

const keysToDelete = [
  STORAGE_KEY.LOGIN,
  STORAGE_KEY.NOTIFICATION_SETTING,
  STORAGE_KEY.ONBOARDING,
  STORAGE_KEY.USER,
  STORAGE_KEY.USER_AGENT,
]; // 削除したいキーのリスト

useEffect(() => {
    const deleteAllSecureStoreData = async () => {
      try {
        await Promise.all(
          keysToDelete.map((key) => SecureStore.deleteItemAsync(key)),
        );
        console.log("すべてのキー情報を削除しました。");
      } catch (error) {
        console.error("セキュアストレージの削除に失敗しました:", error);
      }
    };

    const checkFirstLaunch = async () => {
      try {
        // 初回起動かどうかの確認
        const isFirst = await AsyncStorage.getItem("is_first");

        if (isFirst === null || isFirst === "true") {
          // セキュアストレージのデータを破棄
          deleteAllSecureStoreData();
          console.log(
            "初回起動のため、セキュアストレージのデータを削除しました。",
          );

          // 初回フラグを更新
          await AsyncStorage.setItem("is_first", "false");
        }
      } catch (error) {
        console.error("初回起動のチェック中にエラーが発生しました:", error);
      }
    };

    checkFirstLaunch();
  }, []);

これで初回起動(再インストール含む)後は、Androidと同じ挙動にすることができます。

おわりに

今回expo-secure-storeについての簡単な内部の仕組みと、iOSでストレージを管理するための回避策を紹介しました。知識不足の点もあると思いますが、参考になれば幸いです。

ファンタラクティブテックブログ

Discussion