Rork アプリの起動クラッシュ・白画面 完全解決ガイド
Rorkで作ったアプリが、起動直後にクラッシュしてしまう。または白い画面のまま何も表示されません。これほど絶望的な瞬間はありません。特にリリース直前に起こるこの問題は、リリース日程を大きく狂わせます。
しかし安心してください。起動クラッシュや白画面の問題には、いつも明確な原因があります。ここではRorkアプリで最も多く遭遇する5つの起動クラッシュパターンと、それぞれの根本的な解決方法を、実行可能なデバッグ手順とともに解説します。
起動クラッシュの5つのパターン
あなたのアプリが起動で失敗するパターンは、以下のいずれかです:
| パターン | 症状 | 原因 | 対処時間 |
|---|---|---|---|
| 白画面(ブート) | スプラッシュ後、白い画面のまま | JavaScriptバンドル未読み込み | 15-30分 |
| 即座クラッシュ | 起動直後すぐにアプリが落ちる | ネイティブモジュール接続失敗 | 30-45分 |
| 遅延クラッシュ | 3-5秒後に落ちる | 非同期処理エラー(Promise拒否) | 20-40分 |
| リリースビルドのみクラッシュ | シミュレーターではOK、本番ビルドで落ちる | コード最適化で機能削除(ProGuard/R8) | 45-60分 |
| デバイス依存クラッシュ | 特定デバイス・API レベルのみ | メモリ不足、ミッシングパーミッション | 30-90分 |
それでは、パターン別の診断と解決方法を見ていきます。
パターン1:白画面(JavaScriptバンドル未読み込み)
症状:スプラッシュ画面後、白い画面で固まったまま
このパターンは、React Nativeがあなたのアプリコードを実行できていない状態です。JavaScriptのメインバンドルが、デバイス上で「見つからない」または「破損している」可能性があります。
デバッグ手順
ステップ1:Xcode Consoleで実際のエラーメッセージを確認
# Xcode で「Debug」→「Console」を開く、または以下の
# ターミナルで実行中のアプリログを監視
# iPhone実機のログをリアルタイム表示
xcrun simctl spawn booted log stream --predicate 'process == "YourRorkApp"'
# または、もっと簡単に:Xcode デバッグナビゲータで View ConsoleXcode Consoleに以下のようなエラーが出ていないか確認:
❌ エラー例1:
RCTJSExceptionHandler: TypeError: Cannot read property
'_callID' of undefined
❌ エラー例2:
could not find file: /main.jsbundle at
org.reactjs.native.example:file.jpg
❌ エラー例3:
Could not get transform for main
ステップ2:Metro Bundlerが正常に起動しているか確認
白画面の大半の原因は、Metro バンドラー(JavaScriptコンパイラ)が起動していないことです。
# ステップA:キャッシュをクリア
npx react-native start --reset-cache
# ターミナルに以下が表示されれば OK:
# ✓ To reload the app press r
# ✓ To open developer menu press d
# ステップB:別のターミナルウィンドウでアプリをビルド
npx react-native run-ios
# または
npx react-native run-androidステップ3:JavaScriptファイルに構文エラーがないか確認
Rorkでアプリを生成した後、カスタムコードを追加した場合、JavaScriptエラーが白画面を引き起こす場合があります。
// ❌ 悪い例:構文エラー
function loadData() {
const users = fetchUsers(
// 括弧を閉じていない ← これでバンドル失敗
return users;
}
// ✓ 正しい例
function loadData() {
const users = fetchUsers();
return users;
}構文チェック:
# Node.jsで最小化されたコードで構文エラーをチェック
node -c src/index.js
node -c src/App.js
node -c src/screens/*.js
# エラーが出たら、ファイルを修正して再度試すステップ4:Bundle URLが正しく設定されているか確認
シミュレーターではMetro Bundlerからバンドルを読み込みますが、実機ではバンドルをアプリ内に含める必要があります。
# iOS:本体バンドルを生成
npx react-native bundle \
--platform ios \
--dev false \
--entry-file index.js \
--bundle-output ios/main.jsbundle \
--assets-dest ios
# 生成された ios/main.jsbundle が存在することを確認
ls -lh ios/main.jsbundle解決方法(チェックリスト)
# 白画面の解決テンプレート
# 1. Metroをリセット + キャッシュクリア
pkill -9 node # 既存Metroプロセスを強制終了
rm -rf node_modules
npm install
# 2. 再スタート
npx react-native start --reset-cache
# 3. 別ウィンドウでビルド
npx react-native run-ios --device "[デバイス名]"
# 4. それでも駄目なら、iOS 固有の問題
cd ios && pod install && cd ..
npx react-native run-iosパターン2:即座クラッシュ(ネイティブモジュール接続失敗)
症状:起動直後、ほぼ瞬時にアプリが落ちる
「白画面」ではなく、アプリがすぐに落ちる場合、ネイティブコード(SwiftやJava)側の問題です。通常、以下の原因です:
- CocoaPodsのライブラリが正しくリンクされていない
- iOS: Info.plistに必須キーが不足している
- Android: AndroidManifest.xml に必須パーミッションがない
デバッグ手順
iOS:Xcode Organizer でクラッシュログを確認
# Xcode を開く → Window → Organizer → Crashes
# 最新のクラッシュレポートをダブルクリック
# 例:
# Exception Codes: 0x0000000e (Signal SIGABRT)
# Responsible FrameWork: libRCTCxxBridge
# → CocoaPods or React Native bridging の問題iOS:podfile と pod install を確認
# Rorkで生成されたアプリの場合、まず Pods が
# 正しくインストールされているか確認
cd ios
ls -la Pods/
# Pods ディレクトリが空 or Podfile.lock が古い場合
rm -rf Pods
rm -rf Podfile.lock
pod repo update
pod install
# その後
cd ..
npx react-native run-iosAndroid:adb logcat でエラーを確認
# デバイス接続確認
adb devices
# リアルタイムログ表示
adb logcat | grep -i "crash\|error\|exception"
# 例:
# AndroidRuntime: FATAL EXCEPTION: main
# java.lang.RuntimeException: Unable to start activity
# → ネイティブ初期化エラー解決方法
iOS の場合
# Step 1: CocoaPods をリセット
cd ios
rm -rf Pods Podfile.lock
pod deintegrate
pod install
cd ..
# Step 2: Xcode ビルドキャッシュをクリア
rm -rf ~/Library/Developer/Xcode/DerivedData/*
# Step 3: 再ビルド
npx react-native run-iosAndroid の場合
# Step 1: Gradle キャッシュをクリア
cd android
./gradlew clean
cd ..
# Step 2: node_modules と metro キャッシュをクリア
rm -rf node_modules
npm install
# Step 3: 再ビルド
npx react-native run-androidInfo.plist(iOS)の必須キーを追加
Rorkで生成されたアプリでも、以下のキーが不足していないか確認:
<!-- ios/YourApp/Info.plist -->
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSLocalNetworkUsageDescription</key>
<string>This app uses the local network to communicate.</string>
<key>NSBonjourServices</key>
<array>
<string>_http._tcp</string>
</array>
<!-- 上の3つが特に重要 -->
</dict>パターン3:遅延クラッシュ(非同期エラー)
症状:起動から3-5秒後に落ちる
スプラッシュ画面をくぐりぬけた直後、アプリが落ちる場合、大抵は非同期コード(APIリクエスト、データベース初期化など)でキャッチされていないエラーが発生しています。
デバッグ手順
ステップ1:React Native デバッガーで実行
# デバッグモードで、エラーポップアップを全て表示
npx react-native start
# 別ウィンドウで、デバッガーを起動
npx react-native run-ios
# iOS デバッグメニュー:shake デバイス → Debug
# Logcat(Android)を開いて Watch All Exceptionsステップ2:app.json(または初期化コード)の非同期処理を特定
// ❌ 悪い例:エラーハンドリングなし
export default function App() {
useEffect(() => {
fetchInitialData(); // ← エラーが発生したら?
}, []);
return <Home />;
}
// ✓ 良い例:エラーハンドリングあり
export default function App() {
useEffect(() => {
fetchInitialData()
.catch(error => {
console.warn('Init failed:', error);
// リトライロジック、またはデフォルト値の使用
});
}, []);
return <Home />;
}ステップ3:Promise 拒否をキャッチ
アプリ起動時に Promise が拒否されるとクラッシュします。以下をアプリトップレベルに追加:
// index.js または app.tsx
import React from 'react';
import { AppRegistry } from 'react-native';
import App from './src/App';
// Unhandled Promise Rejections をキャッチ
const errorHandler = (error) => {
console.error('Unhandled Promise Rejection:', error);
};
global.onunhandledrejection = errorHandler;
// または
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});
AppRegistry.registerComponent('YourRorkApp', () => App);ステップ4:アプリ起動シーケンスを可視化
// src/App.js
export default function App() {
const [appReady, setAppReady] = useState(false);
useEffect(() => {
console.log('1. App component mounted');
initializeApp()
.then(() => {
console.log('2. App initialization complete');
setAppReady(true);
})
.catch((error) => {
console.error('3. App initialization failed:', error);
// フォールバック UI を表示
setAppReady(true); // ← エラーでも continue
});
}, []);
if (!appReady) {
return <LoadingScreen />;
}
return <MainApp />;
}
async function initializeApp() {
try {
console.log('Loading database...');
await loadDatabase();
console.log('Loading user data...');
await fetchUserData();
console.log('Loading assets...');
await preloadAssets();
console.log('All initialized!');
} catch (e) {
throw new Error(`Initialization failed: ${e.message}`);
}
}解決方法
# 遅延クラッシュの原因特定フロー
# 1. すべての非同期処理にエラーハンドリングを追加
grep -r "await\|fetch\|then" src/
# 各々に .catch() を追加
# 2. console.error を見守る
npx react-native run-ios 2>&1 | tee debug.log
# 3. エラーメッセージから原因を特定
tail -f debug.log | grep -i "error\|failed\|undefined"
# 4. 該当コードを修正、再テストパターン4:リリースビルドのみクラッシュ
症状:シミュレーターでは完璧に動くが、本番ビルド(AdHoc/Production)で落ちる
これはコード最適化ツール(Android: ProGuard/R8, iOS: Bitcode)が、アプリに必要なコードを「不要」と判定して削除してしまう問題です。
デバッグ手順
iOS:Bitcode を無効化して再ビルド
# ios/Podfile を開く
open ios/Podfile
# 以下を追加
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
# 保存して再インストール
cd ios && pod install && cd ..
npx react-native run-iosAndroid:ProGuard ルールを設定
# android/app/proguard-rules.pro を編集
cat > android/app/proguard-rules.pro << 'EOF'
# React Native
-keep class com.facebook.react.** { *; }
-keep class com.facebook.jni.** { *; }
-keep @interface com.facebook.react.** { *; }
# Keep native methods
-keepclasseswithmembernames class * {
native <methods>;
}
# Your App (重要:Rorkで生成されたパッケージ名)
-keep class net.rorklab.myapp.** { *; }
EOF本番ビルドでテスト
# iOS
cd ios
xcodebuild -workspace YourApp.xcworkspace \
-scheme YourApp \
-configuration Release \
-derivedDataPath build
cd ..
# Android
cd android
./gradlew bundleRelease
# または
./gradlew assembleRelease
cd ..解決方法
# リリースビルド設定の完全チェック
# 1. iOS: build settings を確認
open ios/YourApp.xcodeproj
# Build Settings → Optimization Level を確認
# Debug: Optimization: None を選択
# 2. Android: minifyEnabled を確認
# android/app/build.gradle
# buildTypes { release { minifyEnabled true } }
# ↑ これを true にしたら proguard-rules.pro を追加
# 3. TestFlight や AdHoc でテスト
# 本番環境と同じ署名・設定で配布版ビルド
# 4. もし迷ったら、minify を一度 OFF にしてテスト
# minifyEnabled false でクラッシュしなければ、
# ProGuard/R8 が原因パターン5:デバイス依存クラッシュ
症状:特定のiPhone/Androidデバイス、特定のOSバージョンでのみクラッシュ
新しいデバイス、古いデバイス、特定のメモリ容量のデバイスでのみ起動エラーが発生する場合、以下が原因:
- メモリ不足(アプリが起動時に大量メモリを消費)
- 64-bit vs 32-bit(Android)
- API レベルの互換性(Android)
- iOS バージョン互換性
デバッグ手順
メモリプロファイリング(iOS)
# Xcode → Debug Navigator → Memory
# App launch 時にメモリ使用量をグラフで監視
# 急激に増加 → メモリリーク
# 200MB 以上 → 老朽デバイスで OOM
# コマンドラインで確認
instruments -t "Allocations" -l 10 \
-D build/Logs/Test/Run-YourApp-*/Session.logデバイス仕様の確認(Android)
adb shell getprop ro.build.version.sdk # API Level
adb shell getprop ro.product.cpu.abilist # CPU Architecture
adb shell cat /proc/meminfo | head -1 # 総メモリ
# 例:
# API Level: 24
# ABIs: armeabi-v7a, arm64-v8a
# MemTotal: 3700000 kB最小メモリ使用量への最適化
// ❌ 悪い例:起動時に全画像をメモリロード
const images = allImages.map(url =>
require('./images/' + url) // 全部をメモリに展開
);
// ✓ 良い例:遅延ロード
const loadImage = async (url) => {
const response = await fetch(url);
return response.blob();
};解決方法
# デバイス依存クラッシュの対策
# 1. 複数デバイスで起動テスト
adb devices # 接続中のデバイス一覧
# 各デバイスで順番にビルド
for device in $(adb devices | grep -v 'List\|^$'); do
adb -s $device install -r app.apk
# 起動して数秒待つ
done
# 2. メモリリークチェック
# Xcode: Memory Graph デバッガーで「橙色のアイコン」を探す
# これがリークの候補
# 3. 古いOSバージョン向けの互換性確認
# iOS: Deployment Target を上げすぎていないか
# Android: minSdkVersion を下げてテスト
# build.gradle
android {
defaultConfig {
minSdkVersion 21 // API 21 = Android 5.0 (幅広いデバイス対応)
}
}クラッシュログ解読ガイド
実際のクラッシュレポートから原因を読み取るスキル:
例1:NSInvalidArgumentException(iOS)
*** Terminating app due to uncaught exception 'NSInvalidArgumentException'
reason: 'Cannot find native module for ImagePicker'
→ 対処: pod install を忘れた、または CocoaPods 依存性が不足している
cd ios && pod install && cd ..例2:Module Not Found(Android)
Module not found: "react-native-camera"
at Object.<anonymous> (index.android.bundle:1:2000)
→ 対処: npm パッケージがインストールされていない、または linking が完了していない
npm install react-native-camera
npx react-native link react-native-camera
npx react-native run-android例3:Promise Rejection
Unhandled Promise Rejection: TypeError: Cannot read property
'result' of undefined
→ 対処: APIレスポンスの null チェックが不足している
// ❌ 危険
const result = response.data.result;
// ✓ 安全
const result = response?.data?.result ?? null;
if (!result) {
console.warn('No result from API');
return;
}最終チェックリスト(起動前の確認項目)
# リリース前の起動テストチェックリスト
基本構成:
□ Metro Bundler が起動して「reload ready」を表示している
□ Console に赤いエラーが表示されていない
□ アプリが 5 秒以内にホーム画面を表示
動作テスト:
□ iOS シミュレーター(最新 OS)で起動確認
□ iOS 実機(複数デバイス)で起動確認
□ Android シミュレーター(複数 API Level)で起動確認
□ Android 実機(複数 デバイス)で起動確認
メモリ・パフォーマンス:
□ Instruments で メモリ使用量を確認(初期値 < 50MB)
□ 起動〜ホーム画面表示まで < 3 秒
□ メモリリーク(増え続ける)がないか確認
ビルド・設定:
□ Release Build でもクラッシュしない
□ Info.plist / AndroidManifest.xml に必須キー設定済み
□ Signing Certificate が正しく設定されている
□ Bundle ID / Package Name が正しい
本番環境:
□ 本番 API エンドポイントが正しく設定されている
□ 環境変数 / config が本番値になっている
□ TestFlight / Play Store beta で最終確認済み全体を振り返って
起動クラッシュや白画面は怖く見えますが、原因は限定的です。以下の順序で対処すれば、ほぼすべてが解決します:
- Console のエラーメッセージを見る(最も重要)
- キャッシュをクリア (
--reset-cache, pod install, gradle clean) - デバッグモードで実行 (xcode Debugger, adb logcat, Flipper)
- 最小限のコードまで削除して実行 (二分探索でエラー原因を特定)
- エラーハンドリングを追加 (try-catch, .catch(), error boundaries)
Rorkで生成されたアプリでも、これらの基本デバッグスキルは必須です。マスターすれば、どんな起動エラーでも30分以内に対処できるようになります。