Rork が書いたコードに react-native-mmkv を足して保存したところ、手元の Expo Go でアプリが開かなくなりました。スプラッシュのあとに赤い画面が出て、文言はライブラリやバージョンによって「Native module cannot be null」だったり「Failed to create a new MMKV instance」だったりします。コードを読み返しても間違いが見つからず、Rork の AI に修正を頼んでも直りません。原因がコードの外にあると気づくまでに時間がかかるのが、この症状のやっかいなところだと思います。
この赤い画面は、Rork や Expo の不具合ではありません。Expo Go は、あらかじめ Expo SDK と一緒に組み込まれたネイティブモジュールしか動かせない作りになっています。アプリ専用のネイティブコードが必要なライブラリを入れた時点で、Expo Go の出番は終わるのです。次に使うのが Expo Dev Client、いわゆる「カスタム開発ビルド」です。
ここからは、Rork で生成したアプリに Dev Client を導入する手順と、つまずきやすい点をお伝えします。AI にコードを何度書き直させても直らない場合は、コードではなく実行環境側に原因があることが多いので、最初に切り分ける目印も添えました。
Expo Go と Expo Dev Client の違い
最初に、この2つの位置づけを整理します。
Expo Go は App Store や Google Play で配布されている汎用のプレビューアプリです。書いた JavaScript と、Expo SDK に含まれるネイティブモジュールは動かせますが、自分で追加したサードパーティのネイティブコードは入っていません。
Expo Dev Client は、そのアプリ専用にビルドする開発用のアプリです。中身は本番ビルドにかなり近く、追加したネイティブモジュールもすべて入っています。違いは、JavaScript を Metro の開発サーバーから読み込めること、開発者メニューが使えることです。
つまり Dev Client は「本番ビルドに、リロードの仕組みを足したもの」と考えると理解しやすくなります。一度ビルドして端末に入れてしまえば、JavaScript だけの変更は普段どおり保存してリロードするだけで反映されます。
導入が必要になりやすいライブラリ
Rork のユーザーがよく追加するライブラリのうち、Expo Go では動かないことが多いものを挙げます。
react-native-mmkv:高速なローカルストレージreact-native-purchases(RevenueCat):サブスクリプション課金react-native-vision-camera:高機能なカメラreact-native-keychain:認証情報の安全な保存@react-native-firebase/*:Firebase のネイティブ SDKreact-native-onesignal:プッシュ通知(Rork に OneSignal を組み込むガイド も参照)
一方で、@shopify/react-native-skia のように、Expo SDK のバージョンによっては Expo Go に同梱されているものもあります。「このライブラリは Expo Go で動くのか」は、記憶ではなく、使っている SDK のバージョンの公式ドキュメントで確認するのが確実です。expo-image、expo-secure-store、expo-notifications のように Expo SDK の一部として提供されているものだけで足りているあいだは、Expo Go のまま開発を続けられます。
手順1: 設定を整える(prebuild は必須ではありません)
Dev Client は EAS Build で作るのが最も手軽です。まず app.json に、ビルドに必要な識別子が入っているかを確認します。
{
"expo": {
"name": "MyRorkApp",
"slug": "my-rork-app",
"ios": {
"bundleIdentifier": "com.yourcompany.myrorkapp"
},
"android": {
"package": "com.yourcompany.myrorkapp"
}
}
}識別子は、自分のドメインを逆さに並べた文字列にするのが慣例です。ストアに出したあとは変えにくいので、最初に決めておくことをおすすめします。
ここで、よくある誤解を1つ書き残します。npx expo prebuild は app.json から ios/ と android/ を生成するコマンドですが、EAS Build で作るだけなら手元で実行する必要はありません。EAS 側で必要に応じて実行されます。
むしろ注意したいのは、手元で prebuild して ios/ と android/ をリポジトリにコミットしてしまった場合です。その状態では EAS Build はそのフォルダを正として使い、あとから app.json を直しても反映されなくなります。設定を変えたのにビルドに出てこない、というときは、まずここを疑ってみるのが近道です。
# 手元でネイティブ側の中身を確認したいときだけ実行します
npx expo prebuild --clean--clean は既存の ios/ と android/ を消して作り直します。ネイティブコードを手で編集している場合は、実行前に退避しておくと安心です。
手順2: expo-dev-client を入れて EAS Build を実行する
次に Dev Client 本体を入れて、ビルドの設定を作ります。
npx expo install expo-dev-client
npm install -g eas-cli
eas login
eas build:configureeas build:configure は対話形式で eas.json を作ります。ここに開発ビルド用のプロファイルを足します。
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
}
},
"preview": {
"distribution": "internal"
},
"production": {}
}
}developmentClient: true が、Dev Client 用のビルドであることを示す印です。ios.simulator: true を付けると iOS シミュレータ向けになります。実機に入れたい場合は、この指定を外していただければ大丈夫です。実機向けの iOS ビルドには、Apple Developer アカウントと端末の登録が必要です。
# iOS シミュレータ向け
eas build --profile development --platform ios
# Android(実機・エミュレータ)
eas build --profile development --platform androidクラウドでビルドするため、Mac がなくても iOS 向けのビルドは作れます。ただし実機向けは署名の準備が要ります。所要時間はキューの混み具合で変わり、十数分から、混雑時は数十分かかることもあります。
手順3: Dev Client を起動して開発を始める
ビルドを端末やシミュレータに入れたら、開発サーバーを起動します。
npx expo start --dev-clientCLI のバージョンによっては、expo-dev-client を入れていれば --dev-client を付けなくても Dev Client 向けに起動します。それでも、付けておけば意図が明確で、Expo Go 向けのサーバーが立ち上がる事故を避けられます。ターミナルの QR コードを Dev Client で読み取るか、シミュレータなら i(iOS)や a(Android)を押すと接続できます。
どの変更でリビルドが必要か
Dev Client で最も迷いやすいのが、「今回の変更でビルドし直すべきか」という判断です。次の表を目安にしていただければと思います。
| 変更の内容 | リビルド | 補足 |
|---|---|---|
| 画面や処理など、JavaScript・TypeScript だけの変更 | 不要 | 保存してリロードすれば反映されます |
| ネイティブモジュールの追加・削除・バージョン更新 | 必要 | 新しいネイティブコードは既存のビルドに入っていません |
アプリ名・アイコン・権限・URL スキームなど app.json の変更 | 必要 | ネイティブ側の設定に反映させる必要があります |
| Expo SDK のバージョンを上げた | 必要 | 同時に npx expo install --check で依存の整合も確認します |
| Metro や環境変数だけの変更 | 多くは不要 | 開発サーバーの再起動で足りる場合がほとんどです |
ネイティブ側が変わっているのにリビルドしていないと、起動直後のクラッシュや、新しいモジュールだけが「null」になる症状が出ます。この場合、JavaScript の側をいくら直しても効きません。
EAS Build の回数とローカルビルド
EAS Build には、プランごとに月あたりのビルド枠があります。具体的な枠や料金は変わることがあるため、EAS の料金ページ で最新の内容をご確認いただければ幸いです。毎日のようにネイティブモジュールを入れ替えて試す時期は、枠を使い切りやすくなります。
その時期だけは、手元でビルドする方法もあります。
eas build --profile development --platform ios --local--local を付けると、自分の Mac でビルドが走ります。Xcode と関連ツールの準備が必要ですが、回数を気にせず試せるようになります。なお iOS のローカルビルドは macOS でのみ実行できます。
EAS Update と組み合わせるときの注意
EAS Update(OTA アップデート) を使っている場合は、ランタイムバージョンとチャンネルの扱いに気を付けてください。開発ビルドと本番ビルドが同じチャンネルを見ていると、Dev Client が本番向けの古い JavaScript を取りに行き、直したはずの不具合が戻って見えることがあります。
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"channel": "development"
},
"production": {
"channel": "production"
}
}
}チャンネルは、eas.json のプロファイル側で分けておくと管理しやすくなります。app.json の runtimeVersion は、ネイティブ側が変わらない限り同じ値を保ちます。ネイティブモジュールを足したら、ランタイムバージョンも新しくして、古いビルドに互換性のない更新が届かないようにしてください。
切り分けの順番
導入したのに起動直後に落ちる場合は、原因を深追いする前に、次の4つを上から順に確認してください。
- ネイティブモジュールを足したあと、本当にリビルドして、そのビルドを端末に入れ直しましたか。
- 端末やシミュレータに残っている Dev Client は、最新のビルドですか。古いものが残ったままになりがちです。
- 開発サーバーは
--dev-client付きで起動していますか。Expo Go 向けの画面になっていないかを確認します。 - EAS Update を使っている場合、Dev Client が見ているチャンネルは意図したものですか。
加えて、npx expo-doctor を実行すると、依存のバージョン違いや設定の矛盾を一覧で拾ってくれます。ログを読む前に1回流しておくと、切り分けが早くなります。
Rork で作ったアプリが Dev Client に行き着く理由
Rork が生成するコードは、初期状態では Expo Go の範囲に収まりやすい構成です。まず動かして確かめる体験を優先する設計と考えられ、はじめの一歩としては合理的です。
しかし本格的にリリースを考え始めると、課金、高度なプッシュ通知、高速なストレージなど、ネイティブモジュールが絡む機能が増えていきます。そこで赤い画面に出会い、AI に直させようとして堂々巡りになるのは、よくある流れです。原因が環境側にあると分かれば、AI に直してもらう範囲と、自分で環境を整える範囲を分けて考えられます。
リリースを意識し始めた段階で、一度 Dev Client へ切り替えておくと、あとの作業が楽になります。App Store Connect への申請手順 や ローカルストレージの使い分け も、Dev Client を前提にした内容ですので、あわせてご覧ください。
次にやること
導入が済みましたら、まず小さなネイティブモジュールを1つだけ追加して、ビルドから起動までを通してみていただければと思います。私は、最初の1回を小さく通しておくことが、いちばん効く予防だと考えています。react-native-mmkv のように、影響範囲が狭いものが向いています。1回通せば、リビルドの判断も、赤い画面の切り分けも、感覚としてつかめてきます。
そのうえで RevenueCat やプッシュ通知のような大きなライブラリへ進めば、問題が起きたときも「どの変更が原因か」を絞り込みやすくなります。