minulo は、iOS 26 以前で WKWebView の Cookie ストアがアプリの起動直後に数秒だけ空で返ることを「ログアウトした」ではなく「まだ分からない」と扱うように変えて、Instagram のログインが勝手に消える不具合を直しました(バージョン 1.0.15)。minulo は Instagram のリールや YouTube のショートを消して DM は残す iPhone アプリで、私はその開発者です。minulo がパスワードや Cookie をどう扱っているかは、こちらにまとめています:https://minulo.app/is-minulo-safe.html?ref=qiita-1101
何をしているアプリか(ログインまわりだけ)
minulo は Instagram などのウェブ版を WKWebView で開きます。ログインは各サービスのページで本人がするので、パスワードはアプリに来ません。
複数アカウントに対応するため、ログイン中の Cookie をアカウントごとに端末の中へ控えておき、開いたときにログインの印(Instagram なら sessionid)が無ければ、控えから戻します。サービス側でログアウトしたときに戻し続けないよう、戻したのにログインできていなければ控えを捨てます。
起きていたこと
1.0.13 のころ、「アプリを閉じるたびに Instagram からログアウトされる」というレビューが日本と韓国から続けて付きました。PostHog の数字では、iOS 26 以前の人は開いた 1,057 回のうち 242 回(22.9%)でログアウトが起き、7日間で開いた人の 29.7% が1回以上ログアウトしていました。iOS 27 では 0 回です。
手元の実機は iOS 27.2 だったので、一度も再現しませんでした。
原因
iOS 26 以前では、アプリを起動し直してから2〜3秒のあいだ、WKWebsiteDataStore.default().httpCookieStore.getAllCookies が、ログイン中でも空の配列を返すことがありました。
minulo は WebView を開く前に「ログインの印があるか」を確かめていて、空の配列を「印が無い=ログアウトした」と読んでいました。そこで控えから Cookie を戻すのですが、控えはそのときのログインより古いことがあります。生きているログインを古い控えで上書きした結果、Instagram 側では無効なログインになり、ログイン画面が出ていました。
つまり、守るための仕組みがログアウトを起こしていました。
直し方
1. 「無い」と「分からない」を分ける
サービスはログアウトしていても、端末を見分ける Cookie などを必ず残します。なので、そのサービスのドメインの Cookie が1つも無いなら、ログアウトではなく「ストアがまだ読み込めていない」と見なして nil を返すようにしました。
AsyncFunction("hasSession") { (domains: [String], names: [String], promise: Promise) in
WKWebsiteDataStore.default().httpCookieStore.getAllCookies { cookies in
// そのサービスの Cookie が1つも無い=まだ分からない
if !cookies.contains(where: { Self.matches($0, domains) }) { promise.resolve(nil); return }
promise.resolve(cookies.contains { Self.isSession($0, domains, names) })
}
}.runOnQueue(.main)
2. 開く前は、分からなければ戻さない
開く前の確認では、「無い」が2回続いたときだけ信じ、nil(分からない)のときは戻さずにそのまま開きます。戻すのは今のログインを消す操作なので、迷ったら何もしない側に倒しました。
const r1 = await N.hasSession(c.domains, c.names);
const has = r1 === false ? await N.hasSession(c.domains, c.names) : r1;
if (has === null) { st.unknown = true; } // 戻さずに開く
if (has === false && N.hasSavedSession(s)) { /* 控えから戻す */ }
3. ページを読んだあとに確かめ直す
開く前に分からなかったときは、ページを読み終えたあとにもう一度確かめます。このときはストアにそのサービスの Cookie が入っているので、印が無ければ本当に消えています。1.5秒あけて2回とも無いときだけ、控えから戻して入口から読み直します(1回の表示で1回まで)。
直ったかをどう測っているか
次のイベントを PostHog に送っています(session_unknown は 1.0.15 で足しました)。どれも数字だけで、URL やアカウントの情報は載せていません。
-
session_unknown:開く前に分からなかった回数と、そのときのストアの Cookie の数 -
session_restored:戻した回数と理由(開く前に戻した/ページを読んだあとに戻した) -
session_restore_failed:戻したのにログインできていなかった回数と、控えてから何時間たっていたか
判定は「iOS 26 以前の 1.0.15 で、開いた回数あたりのログアウトが1%未満」としています。公開してまだ数日なので、開いた回数が300回を超えるのを待って判断します。
学んだこと
- 空の結果は「無い」とは限らない。読み込みの途中かもしれない。
- 自分の実機の OS だけで試すと、古い OS でしか起きない不具合は見えない。使っている人の OS の割合を先に見る。
- 「戻す」「消す」のような取り返しのつかない操作は、確信が持てるときだけにする。
- 直す前に、何回起きているかを数えるイベントを入れておくと、直したあとに効き目を同じ物差しで比べられる。