0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Flutter】Firebase x Flaovor ガイド - dSYM ファイルをアップロードしてください、PhaseScriptExecution FlutterFire:\ \"flutterfire\ upload-crashlytics-symbolsも解決

Last updated at Posted at 2025-10-11

はじめに

FlutterでFlavorによる環境分けを行いつつ、FirebaseのCrashlyticsを導入したのですが、エラーに遭遇し、無事解決できたので記事にします。

前提条件

Flavor導入

今回はFlavorを導入したバージョンの手順となります。
一部はFlavorを導入してなくても参考にはなると思います。

Flavorの導入は公式の記事の通りにやりつつ、一部、不明な部分はこの記事を参照して実行してください。

Firebaseを使用する環境

今回はFlavorでlocal,dev,stg,prdを用意し、stgとprdのみFirebaseを使用しています。

Firebase導入

参考記事は下記です。

一部捕捉しつつ、手順を書きます。

  1. 本番用とステージング用のFirebaseのプロジェクトを作成し、必要に応じてアカウントを追加します
  2. npm install -g firebase-toolsFirebase CLIをインストールします
  3. firebase loginで、Firebaseのプロジェクトに参加したGoogleアカウントでログインします
  4. fvm dart pub global activate flutterfire_cliでFlutterFire CLIをインストールする
  5. 以下を実行し、ステージング環境のFirebase構成を行います

--project=firebase_project_stgは作成したFirebaseのプロジェクトのID
--ios-bundle-id=com.example.stgと--android-package-name=com.example.stgは、アプリIDとなります

flutterfire config \
--project=firebase_project_stg \
--out=lib/firebase_options_stg.dart \
--ios-bundle-id=com.example.stg \
--ios-out=ios/flavors/stg/GoogleService-Info.plist \
--android-package-name=com.example.stg \
--android-out=android/app/src/stg/google-services.json
  1. You have to choose a configuration typeには、Build configurationを選択します
  2. Please choose one of the following build configurationsDebug-stgを選択します
  3. もう一度同じコマンドを実行し、Please choose one of the following build configurationsRelease-stgを選択します

二回することで、デバッグビルド時とリリースビルド時用にFirebaseの設定ファイルが更新されます。

  1. 以下を実行し、本番環境のFirebase構成を行います
flutterfire config \
--project=firebase_project_prd \
--out=lib/firebase_options_prd.dart \
--ios-bundle-id=com.example.prd \
--ios-out=ios/flavors/prd/GoogleService-Info.plist \
--android-package-name=com.example.prd \
--android-out=android/app/src/prd/google-services.json
  1. Which platforms should your configuration supportandroidiosを選択する
  2. You have to choose a configuration typeには、Build configurationを選択する
  3. Please choose one of the following build configurationsDebug-prdを選択する
  4. もう一度同じコマンドを実行し、Please choose one of the following build configurationsRelease-prdを選択する

パッケージ追加

Crashlyticsに必要なパッケージを追加します。

flutter pub add firebase_core firebase_crashlytics

iOSで最低OSバージョンを変更

記事執筆時点では15以上にする必要がありました。
今後上がっていくと思いますがビルドエラーのメッセージでいくつにすればいいかわかるはずです。

image.png

Cocoapodsを使用している方はios/Podfileも更新が必要です

platform :ios, '15.0'

Androidで最低OSバージョンを変更

android/app/build.gradle.ktsを変更

plugins {
    ...
}

android {
    defaultConfig {
        ...
        // 記事執筆時点では23以上にする必要がありました。
        // 今後は上がっていくと思いますがビルドエラーでどのバージョンにすればいいかわかります
        minSdk = 23
        ...
    }

AndroidでFirebaseを使わないFlavorの場合、使用しないようにビルド設定を変更

これはオプションですが、私の環境の場合、localとdevではFirebaseを使用しないので
android/app/build.gradle.ktsを変更します

plugins {
    id("com.android.application")
    id("kotlin-android")
    // こんな感じで自動で追加されますが消します
    // START: FlutterFire Configuration
    // id("com.google.gms.google-services")
    // id("com.google.firebase.crashlytics")
    // END: FlutterFire Configuration
    // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
    id("dev.flutter.flutter-gradle-plugin")
}

...省略...

// Firebaseプラグイン適用を条件分岐
val useFirebase = gradle.startParameter.taskNames.none { task ->
    // task名に local または dev が含まれていたら Firebase を無効化
    task.contains("local", ignoreCase = true) ||
    task.contains("dev", ignoreCase = true)
}

// 必要な環境のみ追加
if (useFirebase) {
    apply(plugin = "com.google.gms.google-services")
    apply(plugin = "com.google.firebase.crashlytics")
}

android/settings.gradle.ktsの方は消さなくて問題ないです

iOSでFirebaseを使わないFlavorの場合の設定

XCodeでTargets - Runner - Build Phaseを開きます。
FlutterFire: "flutterfire upload-crashlytics-symbols"FlutterFire: "flutterfire bundle-service-file"の先頭に下記を追加します。
Debug-localの部分は必要に応じて、修正してください。
Firebaseを使うビルド構成のみスクリプトを実行できるようにするため、Firebaseが不要なビルド後世の場合はスキップします。

# 本番とステージング以外ではスキップ
if [ "${CONFIGURATION}" == "Debug-local" ] || [ "${CONFIGURATION}" == "Profile-local" ] || [ "${CONFIGURATION}" == "Release-local" ] || [ "${CONFIGURATION}" == "Debug-dev" ] || [ "${CONFIGURATION}" == "Profile-dev" ] || [ "${CONFIGURATION}" == "Release-dev" ] || [ "${CONFIGURATION}" == "Profile-stg" ] || [ "${CONFIGURATION}" == "Profile-prd" ]; then
    echo "Skipping upload crashlytics symobols for ${CONFIGURATION}"
    exit 0
fi

エントリーファイルを修正する

main.dartを修正し、FirebaseOptionを受け取れるようにします。

// flutterfire configで作成したfirebase_optionsファイルをインポートする
import 'package:your_app/firebase_options_prd.dart';

void main() {
  run(
    firebaseOptions: DefaultFirebaseOptions.currentPlatform,
  );
}

/// runメソッドとして切り出すことで、他のファイルから呼べるようにするう
void run({
  FirebaseOptions? firebaseOptions,
}) {
  WidgetsFlutterBinding.ensureInitialized();
  if (firebaseOptions != null) {
    await Firebase.initializeApp(options: firebaseOptions);
  }
  runApp(
    ...
  );
}

他の環境でも実行できるようにします。

main_stg.dart

// flutterfire configで作成したステージング用のfirebase_optionsファイルをインポートする
import 'package:your_app/firebase_options_stg.dart';
import 'package:your_app/main.dart';

void main() {
  // main.dartで定義したrunを呼び出してアプリを実行
  run(
    firebaseOptions: DefaultFirebaseOptions.currentPlatform,
  );
}

main_dev.dart

// firebaseを使わない環境の場合はインポートしない
// import 'package:your_app/firebase_options_dev.dart';
import 'package:your_app/main.dart';

void main() {
  // main.dartで定義したrunを呼び出してアプリを実行
  run(
    // 必要ないので削除
    // firebaseOptions: DefaultFirebaseOptions.currentPlatform,
  );
}

mainを環境ごとに分けた場合は、flutter run -t lib/main_stg.dart --flavor stgで実行できます。

例外をハンドリングする

main.dart
void run({
  FirebaseOptions? firebaseOptions,
}) {
  WidgetsFlutterBinding.ensureInitialized();
  if (firebaseOptions != null) {
    await Firebase.initializeApp(options: firebaseOptions);
+    // Flutterのフレームワーク内でキャッチされなかった例外
+    FlutterError.onError = (errorDetails) {
+      FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
+      // または以下
+      // FirebaseCrashlytics.instance.recordFlutterError(errorDetails);
+    };
+    // 非同期でキャッチされなかった例外
+    // FlutterErrorでは非同期エラーをキャッチすることはできない
+    PlatformDispatcher.instance.onError = (error, stackTrace) {
+      FirebaseCrashlytics.instance.recordError(
+        error,
+        stackTrace,
+      );
+    };
+    // Flutter外部の例外
+    // runApp前や、ネイティブ、低レベルのDartランタイムエラーなど
+    Isolate.current.addErrorListener(
+      RawReceivePort((List<dynamic> errorAndStacktrace) async {
+        FirebaseCrashlytics.instance.recordError(
+          errorAndStacktrace.first,
+          errorAndStacktrace.last is StackTrace
+              ? errorAndStacktrace.last as StackTrace
+              : null,,
+        );
+      }).sendPort,
+    );

例外をハンドリングする

以下のコードでFlutterErrorのログ送信を確認できます。

TextButton(
  onPressed: () async {
    throw Error();
  },
  child: const Text('Throw Test Exception'),
)

実行

AndroidとiOSで実行し、上記の例外を発生させて、アプリを再起動した後、Crashlyticsのログが追加されている確認してください

クラッシュログを確認する

クラッシュログを確認するには、アプリをクラッシュさせる必要があります。
以下のコードでクラッシュが可能です。

ElevatedButton(
        onPressed: FirebaseCrashlytics.instance.crash,
        child: const Text('Crash'),
      ),

iOSで実行できなかったら

もしiOSで、PhaseScriptExecution FlutterFire:\ \"flutterfire\ upload-crashlytics-symbolsというエラーが出た場合は、Build PhaseのFlutterFire: "flutterfire upload-crashlytics-symbols"を下記のスクリプトで置き換えて再度実行してください。

#!/bin/bash
# 本番とステージング以外ではスキップ
if [ "${CONFIGURATION}" == "Debug-local" ] || [ "${CONFIGURATION}" == "Profile-local" ] || [ "${CONFIGURATION}" == "Release-local" ] || [ "${CONFIGURATION}" == "Debug-dev" ] || [ "${CONFIGURATION}" == "Profile-dev" ] || [ "${CONFIGURATION}" == "Release-dev" ] || [ "${CONFIGURATION}" == "Profile-stg" ] || [ "${CONFIGURATION}" == "Profile-prd" ]; then
    echo "Skipping upload crashlytics symobols for ${CONFIGURATION}"
    exit 0
fi

PATH="${PATH}:$FLUTTER_ROOT/bin:${PUB_CACHE}/bin:$HOME/.pub-cache/bin"

# Flavorに応じてGoogleService-Info.plistのパスを切り替え
if [[ "${CONFIGURATION}" == *"stg"* ]]; then
    GSP_PATH="${PROJECT_DIR}/flavors/stg/GoogleService-Info.plist"
else
    GSP_PATH="${PROJECT_DIR}/flavors/prd/GoogleService-Info.plist"
fi
echo GSP_PATH

if [ -z "$PODS_ROOT" ] || [ ! -d "$PODS_ROOT/FirebaseCrashlytics" ]; then
  echo "Warning: PODS_ROOT not found or FirebaseCrashlytics pod missing. Trying SPM path."
  DERIVED_DATA_PATH=$(echo "$BUILD_ROOT" | sed -E 's|(.*DerivedData/[^/]+).*|\1|')
  PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT="${DERIVED_DATA_PATH}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run"
else
  PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT="$PODS_ROOT/FirebaseCrashlytics/run"
fi

if [ ! -f "$PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT" ]; then
    echo "error: Crashlytics native script not found at $PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT"
    exit 1
fi

if [ ! -f "$GSP_PATH" ]; then
    echo "error: GoogleService-Info.plist not found at $GSP_PATH"
    exit 1
fi

DSYM_PATH="${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}"
if [ ! -d "$DSYM_PATH" ]; then
    echo "error: dSYM file not found at $DSYM_PATH"
fi

echo "Using GoogleService-Info.plist from: $GSP_PATH"
echo "Attempting to run native Crashlytics script directly..."
"$PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT" -gsp "$GSP_PATH" -p ios "$DSYM_PATH"

if [ $? -ne 0 ]; then
    echo "warning: Native Crashlytics script finished with non-zero exit code. Upload might have failed."
fi

echo "Native Crashlytics script execution initiated."

下記の記事を参考に、Flavorごとにパスを解決するように修正したものとなります。

Crashlyticsにログが表示されない

実行は上手くいったが、ログが表示されない場合は、下記を参照し、デバッグロギングを有効にしてください

iOSで実行はできるが、dSYM ファイルをアップロードしてくださいとCrashylyticsに表示される

Build Settingsで実行対象のビルド設定をDWARF wit dSYM Fileに変更してください。

image.png

以下の記事が参考になります。

私の環境では、Strip debug symbols during copyの設定は変えなくても大丈夫でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?