保守を1年半サボったExpo製のアプリを、最新化してリリースするのに半年かかった話
以前リリースした際の記事はこちらです👇
リリース後は1年ほど更新していましたが、実装したかった機能が一通り揃ったところで更新をサボっていました。
久々に追加したい機能を思いついて開発を再開したところ…結果的に半年かけて最新化&リリースすることになったので、その軌跡をまとめます。
結論から
Expo SDK 48 → 53 へアップデートしてリリースした結果はこうなりました 😇
- 修正ファイル数: 1,057
- 追加行数: 41,998
- 対応した issue & PR: 78件
👉 更新前から更新後の差分
👉 対応したissue & PR一覧
バージョンアップを対応したアプリの紹介
アプリ
ダウンロードはこちら。
■ iOS
■ Android
■ LPサイト
■ GitHub
制作者
- 名前: wheatandcat
- 担当: 開発
- 名前: mitsubisi
- 担当: デザイン
コンセプト
リリース時にアプリの紹介記事を書いたので、そちらを参照。
使用している技術
※この記事を読む際の注意
Expoとは?
React Nativeをベースにしたモバイルアプリ開発フレームワーク。
EAS Buildなどの仕組みを通じて、ネイティブ実装を極力意識せずに開発・配布できる強力なプラットフォームです。
1年半サボるとどうなる?
結構な日数開発をサボっていたのもあるので、コードもある程度トレンドに合わせて最新化しつつ1ヶ月くらいでリリースできれば良いかなーと気軽に思っていましたが、
調査の結果、それが甘い見積もりであることを思い知らされることになりました 😱
そもそもデバッグできない編
1. EAS Buildでビルドできない
まず最初にアプリがビルドできませんでした 🤮
ExpoはEAS Buildを使用してビルドします。
ExpoにBuild用のサーバーがあり、ビルド用のコードをアップロードすることでビルドされる仕組みになっている。
そのためローカルにビルドに必要な環境を構築しなくてもアプリが作成できるので非常に便利です。
ただ便利な仕組みが使える代わりに制約が強く最新のバージョンから2バージョン前までしかサポートしないことが多く、今回は最新バージョンがExpo SDK 53に対してアプリは48だった。
当然サポート外なのでサーバー経由ではビルドできなかった。
2. ローカルでも起動できない
正直サーバー経由でビルドできないのは想定していました。ただローカルでのビルドはできるだろうと思っていましたが、実際に試してみたらローカル環境でもビルドができませんでした 🤮
というのも以前のExpoはローカル環境はExpo Go[1]で起動、本番環境でリリースする場合はExpoサーバー経由でビルドが基本的な方式だった。
しかし、今はExpo Goはあくまで簡易的な開発ビルドの立ち位置で、ログイン機能やPush通知などのネイティブに関わる機能が実装されている場合はEAS Build[2]を使用する必要がありローカルでも起動できない状態になっていた。
3. Expoのライブラリが非推奨に
memoirではアプリをログインする場合はAndroidはGoogleアカウントでログイン、iOSはAppleアカウントでログインで実装している。
以前はGoogleログインをする場合は、expo-auth-sessionのexpo-auth-session/providers/google
を使用していた。
しかし、現在は非推奨になっており別の方法に移行する必要があることを知った。
その他のExpoが提供するライブラリでも廃止、仕様変更が発生しており、それらを修正する必要がある。
4. Reactのバージョン問題
Expo SDK 48→53の間でReactが 18→19 に更新。
ReactのバージョンアップによるReact自体影響は少なかったが、1部のライブラリでReact 19で動作しないものがあり、移行は作業が発生。
代表的な例はRecoilで、以下のissueの通りでサポートを終了しており、React 19では動作しないことが報告されている。
開発のトレンドも変わるよね編
1. 推奨ナビゲーションライブラリの変更
以前Expoで開発する場合のナビゲーションライブラリはreact-navigationがもっとも多く使用されてた。
現在はExpo Routerが公式のナビゲーションライブラリが提供されており、こちらが推奨。
しかも、Expo Router
はファイルベースルーティング[3]を採用しており、移行するにはフォルダ構造から変更する必要がある。
2. ディレクトリ構造のトレンドが変わる
以前はフロントエンドのディレクトリ構造はAtomic Designが流行っており、本アプリでも採用していた。
しかし、Atomic Designは長期の開発でのメンテナンス性に難があり、今のトレンドでは無い。
今のトレンドはFeature-Sliced DesignやScreaming Architectureのような普遍的な概念を取り込んだディレクトリ構造で開発するのが主流です。
3. Linter、Formatterのトレンドが変わる
以前はLinter、FormatterはESLintとPrettierを使用していた。
現在の上記の代わりにBiomeを採用するケースが増えている。
特に個人開発ではBiomeのようにシンプルな設計のものを採用するのは、開発作業に集中しやすいのでメリットも大きい。
プラットフォームも変わっているよ編
1. edge-to-edgeが推奨
AndroidのTargetSDKを35にするとAndroid 15以降の端末ではedge-to-edgeが強制的に有効になるため対応が必須になる。
具体的にはAndroidのディスプレイの上下のStatusBar
とNavigationBar
もアプリのレンダリング領域になるので、何も対応しないと以下の画像のように表示されてしまう。
参考画像1[4] | 参考画像2[5] |
---|---|
![]() |
![]() |
2. Expoのapp.config.tsの書き方が変わる
Expoの基本設定はapp.config.ts
に設定。
以前のapp.config.ts
ではhooks
というプロパティに各ライブラリに設定するオプションを記載していたが、今はpluginsに記載する方式になった。
設定方法が変わっている箇所もあり修正が必要である。
3. Google Cloudでサポートされていなくなる
アプリでは無くバックエンドの話だが、バックエンドも若干修正が必要な箇所があったが、そこでも問題が発生。
Push通知を送信するようの処理を Google CloudのCloud Functionsで実装していたが、元々使用していたCloud Functionsの1世代目はサポート終了しており、以下の対応が必要になった。
- Cloud Functionsの1世代目から2世代目のバージョンアップ
- Goのバージョンアップ(Go 1.15 → Go 1.23)※2世代目ではGo 1.15をサポートしていない
4.Play Storeのサポートバージョンが変わる
Play Storeから以下の通知が飛んできた。
本来は、単純にAndroidのgradleの設定を変更して再リリースすれば問題ない話だったが、上記のとおりビルドすらできない状態なので、これはピンチ 🫠
上記を踏まえて、どういう対応をしたのか?
方針
ちょっとした修正くらいで動く感じもしなかったので、もういっそ最新化してしまおうという方針で対応した!
最新化の構成の比較
対象 | 変更前 | 変更後 |
---|---|---|
Expo SDK | 48 | 53 |
ナビゲーション | react-navigation | expo-router |
ディレクトリ構造 | Atomic Design | Screaming Architecture |
Lint、Formatter | ESLint、Prettier | Biome |
グルーバルステート | Recoil | Zustand |
各対応内容
デバッグができるようになるまで編
1. ローカルで起動できるように修正
Expoにはバージョンアップ時にマイグレーションを自動で行なうコマンドが用意されているので以下のコマンドを実行。
$ npx expo install expo@latest
$ npx expo install --fix
その上で、以下のコマンドを実行してローカルで起動できるか確認。
$ npx expo-doctor
出力された警告を修正して以下のコマンドで実行。
$ npx expo run:ios
で、起動して欲しかったが、今回のケースは前述のとおり、根本から改修しないといけなかったので、当然起動しなかった 😓
なので、以下のドキュメントを参考にExpo Routerのサンプルアプリを作成して、それをベースに徐々に既存のコードを移行していった。
ここの修正PRはコチラ。
詳細な作業内容は以下のブログに記載。
👉 Expoアプリ最新化①:SDKアップデートとexpo-routerへの移行
2. ディレクトリ構成の修正
更新前
Atomic Designをベースに以下のディレクトリ構造をしていた。
.
└── src
├── components
│ ├── atoms
│ ├── molecules
│ ├── organisms
│ ├── pages
│ └── templates
├── containers
├── hooks
├── img
├── lib
├── queries
└─ store
更新後
ファイルベースルーティング & Screaming Architectureをベースに以下のディレクトリ構造に修正。
.
├── app
│ └── (app)
│ ├── items
│ ├── my-page
│ ├── search
│ └── settings
├── components
│ ├── elements
│ └── layouts
├── containers
├── features
│ ├── home
│ │ └── components
│ └── search
│ ├── components
│ └── hooks
├── hooks
├── lib
├── queries
└── store
3. ログインできるように
ローカルで起動できるようになったので、次はログイン機能を修正。
既存のexpo-auth-session/providers/google
でのGoogleログインはサポートされなくなったので、@react-native-google-signin/google-signinを使用した実装に移行。
最終的には以下の画像のとおり問題なくログインできるようになった。
前 | ログイン | 後 |
---|---|---|
![]() |
![]() |
![]() |
ここの修正PRはコチラ
詳細な作業内容は以下のブログに記載。
👉 Expoアプリ最新化②:Expo SDK52でGoogleログインを実装
4. EAS Buildでビルドできるように
本番環境のアプリを作成して検証が行えるようにEAS Buildを使用したビルドを行えるように修正した。
ローカルビルドでは問題なかったが、app.config.ts
の書き方が正しくないとエラーになっていたので最新のドキュメントを確認してビルドできるようになった。
ここの修正PRはコチラ
検証の準備編
1. Linter、Formatterの修正
検証開始後にLinter、Formatterで悩みたくなかったので、先にESLint & PrettierからBiomeに移行した。
2. CIの復活
CIでテスト、Lint、type check、testを行っていたが、GitHub Actionsで使用していたモジュールのバージョンが非推奨になっており動作してなかったので修正した。
ここの修正PRはコチラ
3. Recoil → Zustandへの移行
RecoilはReact 19で動作しないので、Zustandに移行した。
ここの修正PRはコチラ。
詳細な作業内容は以下のブログに記載。
👉 Recoil→Zustandに移行
4. Storybookの復活
検証中のUI修正に使用するためにStorybookを復活。
Storybook v6からv8にアップデートして動作可能になった。
ここの修正PRはコチラ。
詳細な作業内容は以下のブログに記載。
👉 expo-routerを使ったアプリでstorybook v8を導入
5. Sentryの復活
検証中の不具合の調査に使用するためにSentryを復活。
元はexpo/sentry-expoを使用して実装していたが、現在は非推奨だったため@sentry/react-nativeに移行した。
ここの修正PRはコチラ。
詳細な作業内容は以下のブログに記載。
👉 Expo SDK 52でSentry導入
検証編
1. Qaseで管理しているテストケースを実行
memoirではQaseを使用して手動のテストケースを管理していた。
以下にQaseのテストケース管理画面を載せておく。
2. バグを見つけ次第issue作成
以下のissueにSub-issueを作成して管理。
検証で洗い出したバグは 28件。その一部を以下で紹介。
3. 主な修正したバグ
スプラッシュスクリーンのサイズがおかしい
- スプラッシュスクリーンが異常に小さく表示される
- 以下のissueの通りでスプラッシュスクリーンの表示で使用している
expo-splash-screen
がフルサイズのスクリーンの画像をサポートしなくなったので指定する画像を修正
バグ発生時 | 改修後 |
---|---|
![]() |
![]() |
iOSでreact-native-picker-selectが動いていないので修正
- iOSで
react-native-picker-select
をタッチしてもドロップメニューが表示されない - 以下のissueに記載の通りExpo SDK 52から発生。コメントに記載の通りで
{ inputIOSContainer: { pointerEvents: "none" } }
を追加することで解消した
バグ発生時 | 改修後 |
---|---|
![]() |
![]() |
iOSで日付編集ができない
- DatePickerを動かすとUIが閉じてしまう
- iOSのバージョンアップの影響だったので
react-native-modal-datetime-picker
を最新にして書き方を修正したら解決した
参考画像 |
---|
![]() |
ローカルPush通知が設定できていない
- Expo SDK48→53の間でexpo-notificationsでのWeeklyでローカルPush通知を実装する書き方が変わっていたので、最新の書き方に修正したら解決した
参考画像 |
---|
![]() |
iOSの曜日設定のドロップメニューの色が誤っている
- iOSでダークモードをONにすると発生
- 既存で使用していたreact-native-picker-selectがダークモードをサポートした影響で発生
- 以前はダークモードをサポートしていなかったので自前でCSSをカスタマイズして対応していたが、バージョンアップ時にそのせいで表示がおかしくなっていた
- propsの
darkTheme
を正しく設定することで解決
バグ発生時 | 改修後 |
---|---|
![]() |
![]() |
タスク登録の入力画面を開いたときにテキスト入力にフォーカスが設定されていない
- 以前は「React NavigationでTextInputのautoFocusが効かない」不具合があり独自実装で回避していた
- 現在はこの不具合は解決されているので、
autoFocus
を設定するだけで解決するようになった
参考画像 |
---|
![]() |
iOSで振り返りの共有機能が動作していない
- 振り返りで表示しているコンポーネントを
react-native-view-shot
で画像かして共有する機能が動作していなかった - かなり特殊な対応で、前までは
react-native-view-shot
がiOSの制約で8200pxの高さを超えるスクリーンショットを生成できなかったので独自に対応をしていたのが原因だった - 現在はライブラリ側で対応されており、
props.options.useRenderInContext = true
に設定するだけで正常に動作するようになった
参考画像 |
---|
![]() |
Androidのedge-to-edge起因で発生したバグ
- edge-to-edge関連で対応したissue
- 基本は
react-native-safe-area-context
から取得したtop、bottomの値を使用して位置を調整 - また使用しているライブラリにedge-to-edge対応用のオプションが追加されていて、それを設定するだけで対応できるケースもあった
バグ発生時 | 改修後 |
---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Androidで「今週のmemoirを確認する」とナビゲーションボタンが重なってタッチできない
- こちらも根本原因はedge-to-edge
- Androidの「3ボタン ナビゲーション」に設定すると、ナビゲーションボタンとUIが重なってタッチできないケースがあった
-
react-native-safe-area-context
から取得した値でbottomの位置を調整をして修正
バグ発生時 | 改修後 |
---|---|
![]() |
![]() |
バグ対応を終えて
上記のバグ改修が完了して、2025年8月16日に無事memoir v1.9.0としてストアにリリースできました!
まとめ
Expoは更新をサボると辛い
素のReact NativeのバージョンアップはiOS/Androidのネイティブコードを修正しなくてはならないケースが多く辛いです。
それに比較すると一般的にはExpoを使用することでネイティブコードを修正する必要がなくなるので、バージョンアップの負担が軽減されます。
ただ上記の条件が当てはまるのはExpoの最新を追っている状態を保てている場合の話で、年単位で開発期間が空いた場合は、むしろプラットフォームの縛りが強い分バージョンアップ作業が大変になりました 🥲
今回のような愚直な対応方法ではなくBare Workflowに移行してExpoのEAS Buildに依存しないようにしてビルドするなどの迂回策もあります。
とは言え、フルにExpoのプラットフォームを活用するなら最新をキャッチアップしているのは前提になるので、サボってはいけないなと反省。
AIでのバグ改修も有効
edge-to-edge周りのバグ改修ではClaude Codeが思った以上に役に立ちました。
例えばAndroidでダイアログを開くとbottomが不自然になっているのバグ対応は、自分でやってみて原因が分からなかったので、試しにClaude Codeにissueを渡して対応させてみました。
そしたら、Modal
コンポーネントのpropsにstatusBarTranslucent
があるのを見つけてきて、それを設定することでedge-to-edgeが有効な場合でも、正常にレンダリングできるようになりました。
似たような感じで、edge-to-edge系の対応はClaude Codeで結構対応できました。プロジェクト固有では無いバグ対応にはAIは、かなり有効でした!
最後に
「アプリは何もしなくても壊れる」ことを身をもって体験しました 😱
Discussion