There are few moments more frustrating in mobile development than a Flutter project that compiles perfectly on Android but refuses to build on iOS.
If you have recently added firebase_core, firebase_auth, or cloud_firestore to your pubspec.yaml, you are likely staring at a wall of red text in Xcode or your terminal. The error usually manifests as Module 'firebase_core' not found, ld: symbol(s) not found for architecture x86_64, or a cryptic CocoaPods version mismatch regarding IPHONEOS_DEPLOYMENT_TARGET.
This is not a problem with your code logic. It is a dependency management conflict between Flutter’s build system, CocoaPods, and the evolving requirements of the Firebase iOS SDKs.
This guide provides a rigorous root cause analysis and a definitive, copy-paste solution to align your build targets and fix the CocoaPods integration.
The Root Cause: Deployment Target Drift and Linkage
To solve this permanently, we must understand the friction points between Flutter and native iOS dependencies.
1. The Deployment Target Mismatch
Newer versions of the Firebase iOS SDK (specifically the underlying GoogleUtilities and GoogleDataTransport libraries) now require iOS 13.0 or higher.
However, when you create a new Flutter project, the default iOS template often sets the minimum deployment target to iOS 11.0 (or even lower in older Flutter versions).
When CocoaPods attempts to resolve dependencies, it detects that the Firebase transitive dependencies require iOS 13+. Since your project declares a lower version, CocoaPods fails to find a compatible set of libraries, or Xcode fails the build because the included libraries contain API calls not supported by your project's minimum version.
2. Static vs. Dynamic Linkage
Swift libraries (like Firebase) traditionally prefer dynamic frameworks (use_frameworks!). However, many older Flutter plugins are distributed as static libraries.
Mixing static libraries and dynamic frameworks in a single iOS build ecosystem is notoriously difficult. If the linkage isn't explicitly defined, header paths get lost, resulting in the dreaded Module 'firebase_core' not found.
The Fix: Step-by-Step
We will resolve this by enforcing a modern deployment target across the entire dependency graph and configuring static linkage for Swift pods.
Step 1: Clean the Build Artifacts
Before applying configuration changes, ensure you aren't fighting against cached derived data. Open your terminal in the root of your Flutter project:
# Clean Flutter artifacts
flutter clean
# Navigate to iOS directory
cd ios
# Remove Pods and the lockfile to force a complete recalculation
rm -rf Pods
rm Podfile.lock
Step 2: Configure the Podfile
This is the most critical step. Open ios/Podfile in your preferred editor. You need to modify the file to handle two things:
- Static Linkage: Allow Firebase to interact with other static Flutter plugins.
- Version Enforcement: A
post_installhook to force every dependency to use at least iOS 13.0.
Replace or update your Podfile content to match the configuration below. Pay close attention to the post_install block.
# ios/Podfile
platform :ios, '13.0' # 1. Update the platform version here
# CocoaPods Analytics is not necessary for builds
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1] if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig and running flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
# 2. Use static linkage for frameworks.
# This resolves the vast majority of "module not found" errors for Firebase
# when used alongside other Flutter plugins.
use_frameworks! :linkage => :static
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# 3. Force all third-party pods to target iOS 13.0
# This prevents the "IPHONEOS_DEPLOYMENT_TARGET" mismatch errors
target.build_configurations.each do |config|
# Check current target version. If it's lower than 13.0, bump it up.
current_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET']
# Parse the version as a float to compare
if current_target.to_f < 13.0
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
end
end
Step 3: Update Xcode Project Settings
The Podfile handles the dependencies, but your main "Runner" project must also align with this version.
- Open
ios/Runner.xcworkspacein Xcode. - Select Runner (the blue icon at the top left).
- Select the Runner Target (under "Targets").
- Go to the General tab.
- Under Deployment Info, set Minimum Deployments (iOS) to 13.0.
Step 4: Reinstall and Build
Return to your terminal (inside the ios directory) and regenerate the dependency graph.
# Install pods with the new configuration
pod install --repo-update
# Return to root and run
cd ..
flutter run
Deep Dive: Why The post_install Hook is Mandatory
You might wonder why changing the global platform at the top of the Podfile (platform :ios, '13.0') isn't sufficient.
When you run pod install, CocoaPods reads the .podspec file of every library you use. Many older libraries have .podspec files that declare support for iOS 9.0 or 10.0. CocoaPods generates a separate build target for each of these libraries within the Pods project.
If a library targets iOS 9.0, but it tries to import a Google utility that requires iOS 13.0, the build fails.
The post_install hook in the Ruby script above acts as a final override. It iterates through every single target generated by CocoaPods—regardless of what the original library author defined—and manually rewrites the IPHONEOS_DEPLOYMENT_TARGET build setting to 13.0. This ensures the entire graph is synchronized.
Common Edge Case: Apple Silicon (M1/M2/M3)
If you are on an Apple Silicon Mac and the solution above results in an architecture error (e.g., mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')), your CocoaPods setup might be running via Rosetta while Xcode is trying to build native.
The FFI Fix
Flutter uses Dart's FFI (Foreign Function Interface) to manage CocoaPods interaction. On Apple Silicon, verify you are using the correct architecture for the FFI gem.
Run this inside your project root:
sudo arch -x86_64 gem install ffi
arch -x86_64 pod install --project-directory=ios
However, strictly relying on Rosetta (x86_64) commands is becoming deprecated. The ideal solution is ensuring your terminal and CocoaPods environment are native arm64. If pod install fails specifically on the ffi gem, try reinstalling it specifically for your architecture:
gem install --user-install ffi -- --enable-libffi-alloc
Summary
The module 'firebase_core' not found error is rarely about the module missing; it is about the build system ignoring the module because of version or linkage incompatibilities.
By standardizing your Podfile to use Static Linkage and enforcing a Minimum Deployment Target of iOS 13.0 via a post-install hook, you eliminate the ambiguity that causes these build failures. This setup is robust, future-proof, and aligns with the latest requirements from the Firebase team.