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ボタンを押すとアプリがクラッシュ
原因:
-
Info.plist
のYOUR_CLIENT_ID
プレースホルダーが残っている - Firebase ConsoleでiOSアプリが登録されていない
- Cloud Firestore APIが無効
解決策:
- 実際のClient IDでプレースホルダーを置き換え
- Firebase ConsoleでiOSアプリを登録
- 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アプリの登録
- Firebase Consoleでプロジェクトを作成
- 「プロジェクトの設定」→「マイアプリ」→「アプリを追加」→「iOS」
- バンドルID:
com.app.matching.matchingApp
を入力 -
GoogleService-Info.plist
をダウンロード
2.2 Google認証の有効化
- 「Authentication」→「Sign-in method」→「Google」を有効化
- プロジェクトのサポートメールを設定
2.3 Cloud Firestore Databaseの有効化
- 「Firestore Database」→「データベースを作成」
- 「テストモードで開始」または「本番環境で開始」を選択
- リージョンを選択(例:
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,
// ),
よくあるエラーと対処法
-
"No app has been configured yet"
-
Firebase.initializeApp()
が呼ばれていない -
main.dart
でFirebase初期化を確認
-
-
"Permission denied: Cloud Firestore API has not been used"
- Firebase ConsoleでCloud Firestore Databaseを有効化
-
"Navigator operation requested with a context that does not include a Navigator"
-
if (mounted)
チェックを追加 - コンテキストの有効性を確認
-
重要なポイント
gRPCモジュールマップの問題
-
gRPC-Core.modulemap
とgRPC-C++.modulemap
が正しい場所にコピーされていない -
post_install
フックで自動的にコピーする必要がある - ファイルの同一性チェックで重複コピーを防ぐ
静的リンクの重要性
-
use_frameworks! :linkage => :static
で静的リンクを有効化 - これにより、Firebase関連のフレームワークが正しくリンクされる
iOSデプロイメントターゲット
- iOS 15.0以上を指定することで、最新のFirebase SDKとの互換性を確保
Google認証の設定順序
- Firebase ConsoleでiOSアプリを登録
- Google認証を有効化
-
GoogleService-Info.plist
をダウンロード - Client IDを
Info.plist
に設定 - Cloud Firestore Databaseを有効化
参考リンク
まとめ
Google認証を追加する際のiOSビルドエラーは、以下の要因が重なって発生します:
- gRPC依存関係の問題: モジュールマップの配置と静的リンク
- Firebase設定の不完全性: iOSアプリの未登録、APIの無効化
- Client ID設定の不備: プレースホルダーの残存
上記の設定を行うことで、Google認証を含むFirebaseアプリのiOSビルドが安定し、リリースモードでも正常に動作するようになります。
特に重要なのは:
- PodfileでのgRPCの明示的な指定とモジュールマップの自動コピー
- 静的リンクの有効化
- Firebase Consoleでの適切な設定
- Client IDの正しい設定
これらの設定を行うことで、Google認証を使用したFlutterアプリのiOSビルドが安定します。