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 iOS ビルドエラー解決備忘録(Google認証追加編)

Posted at

Flutter iOS ビルドで発生した gRPC-Core.modulemap エラーの完全解決策 - Google認証追加時のトラブルシューティング

概要

FlutterアプリにGoogle認証を追加しようとした際に、iOSビルドで gRPC-Core.modulemap が見つからないエラーが発生しました。このエラーは複数の要因が重なって発生し、解決までに時間がかかりました。備忘録として解決策を記録します。

発生したエラー

'/Users/username/Documents/project/ios/Pods/Headers/Private/grpc/gRPC-Core.modulemap' not found
Error (Xcode): module map file '/Users/username/Documents/project/ios/Pods/Headers/Private/grpc/gRPC-C++-dummy.m module map file' not found
Framework 'FirebaseFirestoreInternal' not found
Linker command failed with exit code 1

環境

  • Flutter: 3.24.0
  • iOS: 15.0+
  • Firebase: firebase_core ^4.0.0, firebase_auth ^6.0.0, cloud_firestore ^6.0.0
  • google_sign_in: ^6.1.5

問題の経緯

1. Google認証の追加

最初に pubspec.yaml に以下の依存関係を追加しました:

dependencies:
  firebase_core: ^4.0.0
  firebase_auth: ^6.0.0
  cloud_firestore: ^6.0.0
  google_sign_in: ^6.1.5

2. 認証画面の実装

lib/screens/auth_screen.dart にGoogle Sign-Inボタンを追加:

Future<void> _signInWithGoogle() async {
  setState(() {
    _isLoading = true;
  });

  try {
    final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
    if (googleUser == null) {
      print('Googleサインインがキャンセルされました');
      return;
    }

    final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
    final credential = GoogleAuthProvider.credential(
      accessToken: googleAuth.accessToken,
      idToken: googleAuth.idToken,
    );

    final auth = FirebaseAuth.instance;
    if (auth.currentUser != null && auth.currentUser!.isAnonymous) {
      await auth.currentUser!.linkWithCredential(credential);
    } else {
      await auth.signInWithCredential(credential);
    }

    // Firestoreにユーザー情報を保存
    final user = FirebaseAuth.instance.currentUser;
    if (user != null) {
      final userDoc = FirebaseFirestore.instance
          .collection('users')
          .doc(user.uid);
      await userDoc.set({
        'uid': user.uid,
        'email': user.email,
        'displayName': user.displayName,
        'createdAt': FieldValue.serverTimestamp(),
        'updatedAt': FieldValue.serverTimestamp(),
        'provider': 'google',
      }, SetOptions(merge: true));
    }

    print('Google認証成功: ${user?.email ?? 'email不明'}');
    _navigateToMain();
  } catch (e) {
    print('Google認証エラー: $e');
    if (mounted) {
      _showErrorDialog('Googleサインインに失敗しました: $e');
    }
  } finally {
    if (mounted) {
      setState(() {
        _isLoading = false;
      });
    }
  }
}

3. iOS設定ファイルの追加

ios/Runner/Info.plist にGoogle Sign-In用の設定を追加:

<!-- Google Sign-In URL Scheme -->
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>google</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>com.googleusercontent.apps.YOUR_CLIENT_ID</string>
        </array>
    </dict>
</array>

<!-- Google Sign-In 設定 -->
<key>GIDClientID</key>
<string>YOUR_CLIENT_ID</string>

発生した問題と解決策

問題1: gRPC-Core.modulemap が見つからない

症状: iOSビルド時にgRPC関連のモジュールマップファイルが見つからないエラー

原因: Firebaseの依存関係(gRPC)のモジュールマップが正しい場所に配置されていない

解決策: Podfileの post_install フックでモジュールマップを自動コピー

問題2: Framework 'FirebaseFirestoreInternal' not found

症状: リンク時にFirebase関連のフレームワークが見つからない

原因: 静的リンクが有効になっていない、gRPCの依存関係が正しく設定されていない

解決策: use_frameworks! :linkage => :static の追加とgRPCの明示的な指定

問題3: Google認証でクラッシュ

症状: Google Sign-Inボタンを押すとアプリがクラッシュ

原因:

  1. Info.plistYOUR_CLIENT_ID プレースホルダーが残っている
  2. Firebase ConsoleでiOSアプリが登録されていない
  3. Cloud Firestore APIが無効

解決策:

  1. 実際のClient IDでプレースホルダーを置き換え
  2. Firebase ConsoleでiOSアプリを登録
  3. Cloud Firestore Databaseを有効化

完全な解決策

1. Podfileの修正

ios/Podfile に以下の設定を追加:

# ファイルの先頭付近
use_modular_headers!
use_frameworks! :linkage => :static

target 'Runner' do
  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
  
  # gRPCの依存関係を明示的に指定
  pod 'gRPC-Core', :modular_headers => true
  pod 'gRPC-C++', :modular_headers => true
  
  target 'RunnerTests' do
    inherit! :search_paths
  end
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    
    # ビルド設定の調整
    target.build_configurations.each do |config|
      # iOSデプロイメントターゲットの設定
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
      
      # ヘッダー検索パスの調整
      if config.build_settings['HEADER_SEARCH_PATHS']
        config.build_settings['HEADER_SEARCH_PATHS'] ||= []
        config.build_settings['HEADER_SEARCH_PATHS'] << '$(PODS_ROOT)/Headers/Private/grpc'
      end
    end
    
    # すべてのターゲットで-Gフラグ除去
    target.build_configurations.each do |config|
      # BoringSSL-GRPC専用設定
      if target.name == 'BoringSSL-GRPC'
        config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'] = 'NO'
        config.build_settings['GCC_WARN_64_TO_32_BIT_CONVERSION'] = 'NO'
        config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO'
        config.build_settings['GCC_WARN_ABOUT_UNINITIALIZED_VARIABLES'] = 'NO'
        config.build_settings['GCC_WARN_UNUSED_FUNCTION'] = 'NO'
        config.build_settings['GCC_WARN_UNUSED_VARIABLE'] = 'NO'
      end
    end
  end
  
  # iOSデプロイメントターゲットの設定
  installer.generated_projects.each do |project|
    project.targets.each do |target|
      target.build_configurations.each do |config|
        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
      end
    end
  end
  
  # gRPC-Coreのモジュールマップパス修正
  grpc_modulemap_src = File.join(installer.sandbox.root, 'Target Support Files/gRPC-Core/gRPC-Core.modulemap')
  grpc_modulemap_dst = File.join(installer.sandbox.root, 'Headers/Private/grpc/gRPC-Core.modulemap')
  
  if File.exist?(grpc_modulemap_src)
    FileUtils.mkdir_p(File.dirname(grpc_modulemap_dst))
    # ファイルが存在しない場合のみコピー
    unless File.exist?(grpc_modulemap_dst) && FileUtils.identical?(grpc_modulemap_src, grpc_modulemap_dst)
      FileUtils.cp(grpc_modulemap_src, grpc_modulemap_dst)
      puts "✅ gRPC-Core modulemap copied to #{grpc_modulemap_dst}"
    else
      puts "ℹ️  gRPC-Core modulemap already exists and identical at #{grpc_modulemap_dst}"
    end
  else
    puts "⚠️  gRPC-Core modulemap source not found at #{grpc_modulemap_src}"
  end
  
  # gRPC-C++のモジュールマップパス修正
  grpcpp_modulemap_src = File.join(installer.sandbox.root, 'Target Support Files/gRPC-C++/gRPC-C++.modulemap')
  grpcpp_modulemap_dst = File.join(installer.sandbox.root, 'Headers/Private/grpc/gRPC-C++.modulemap')
  
  if File.exist?(grpcpp_modulemap_src)
    FileUtils.mkdir_p(File.dirname(grpcpp_modulemap_dst))
    # ファイルが存在しない場合のみコピー
    unless File.exist?(grpcpp_modulemap_dst) && FileUtils.identical?(grpcpp_modulemap_src, grpcpp_modulemap_dst)
      FileUtils.cp(grpcpp_modulemap_src, grpcpp_modulemap_dst)
      puts "✅ gRPC-C++ modulemap copied to #{grpcpp_modulemap_dst}"
    else
      puts "ℹ️  gRPC-C++ modulemap already exists and identical at #{grpcpp_modulemap_dst}"
    end
  else
    puts "⚠️  gRPC-C++ modulemap source not found at #{grpc_modulemap_src}"
  end
end

2. Firebase Console設定

2.1 iOSアプリの登録

  1. Firebase Consoleでプロジェクトを作成
  2. 「プロジェクトの設定」→「マイアプリ」→「アプリを追加」→「iOS」
  3. バンドルID: com.app.matching.matchingApp を入力
  4. GoogleService-Info.plist をダウンロード

2.2 Google認証の有効化

  1. 「Authentication」→「Sign-in method」→「Google」を有効化
  2. プロジェクトのサポートメールを設定

2.3 Cloud Firestore Databaseの有効化

  1. 「Firestore Database」→「データベースを作成」
  2. 「テストモードで開始」または「本番環境で開始」を選択
  3. リージョンを選択(例:asia-northeast1 (Tokyo)

3. Client IDの設定

GoogleService-Info.plist から取得したClient IDで Info.plist を更新:

<!-- 例 -->
<string>com.googleusercontent.apps.357239775641-08iism51nt02qqdu5gm66gibbutr8miu</string>
<string>357239775641-08iism51nt02qqdu5gm66gibbutr8miu.apps.googleusercontent.com</string>

4. 依存関係のクリーンアップ

cd ios
rm -rf Pods Podfile.lock
pod install
cd ..
flutter clean
flutter pub get

トラブルシューティング

一時的な対処法

Google認証の設定が完了するまで、他の認証方法(Apple Sign-In、ゲストログイン)でテスト:

// Google Sign-Inボタンを一時的に無効化
// _buildSignInButton(
//   'Googleでサインイン',
//   'assets/google_logo.png',
//   Colors.white,
//   Colors.black87,
//   _signInWithGoogle,
// ),

よくあるエラーと対処法

  1. "No app has been configured yet"

    • Firebase.initializeApp() が呼ばれていない
    • main.dart でFirebase初期化を確認
  2. "Permission denied: Cloud Firestore API has not been used"

    • Firebase ConsoleでCloud Firestore Databaseを有効化
  3. "Navigator operation requested with a context that does not include a Navigator"

    • if (mounted) チェックを追加
    • コンテキストの有効性を確認

重要なポイント

gRPCモジュールマップの問題

  • gRPC-Core.modulemapgRPC-C++.modulemap が正しい場所にコピーされていない
  • post_install フックで自動的にコピーする必要がある
  • ファイルの同一性チェックで重複コピーを防ぐ

静的リンクの重要性

  • use_frameworks! :linkage => :static で静的リンクを有効化
  • これにより、Firebase関連のフレームワークが正しくリンクされる

iOSデプロイメントターゲット

  • iOS 15.0以上を指定することで、最新のFirebase SDKとの互換性を確保

Google認証の設定順序

  1. Firebase ConsoleでiOSアプリを登録
  2. Google認証を有効化
  3. GoogleService-Info.plist をダウンロード
  4. Client IDを Info.plist に設定
  5. Cloud Firestore Databaseを有効化

参考リンク

まとめ

Google認証を追加する際のiOSビルドエラーは、以下の要因が重なって発生します:

  1. gRPC依存関係の問題: モジュールマップの配置と静的リンク
  2. Firebase設定の不完全性: iOSアプリの未登録、APIの無効化
  3. Client ID設定の不備: プレースホルダーの残存

上記の設定を行うことで、Google認証を含むFirebaseアプリのiOSビルドが安定し、リリースモードでも正常に動作するようになります。

特に重要なのは:

  1. PodfileでのgRPCの明示的な指定とモジュールマップの自動コピー
  2. 静的リンクの有効化
  3. Firebase Consoleでの適切な設定
  4. Client IDの正しい設定

これらの設定を行うことで、Google認証を使用したFlutterアプリのiOSビルドが安定します。

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?