There are few things more demoralizing in mobile development than a build failure after a clean install. For React Native developers integrating ad mediation, specifically react-native-appodeal, the process often hits two distinct walls: the cryptic linker command failed with exit code 1 on iOS, and the "Module was compiled with an incompatible version of Kotlin" error on Android builds targeting Kotlin 2.x.
These errors aren't just syntax typos; they are fundamental conflicts in how modern mobile build systems (Gradle and CocoaPods) handle binary compatibility and static linking.
This guide provides the architectural root cause analysis and the copy-paste solutions to resolve these specific build blockages in React Native 0.73+.
The iOS Root Cause: Static Linking vs. Swift Symbols
The error linker command failed with exit code 1 is a catch-all failure from the ld (Linker) process in Xcode. However, when working with Appodeal and React Native, the issue is almost specific: Symbol Visibility.
React Native (since 0.71) prefers static linkage. However, many ad networks bundle their SDKs as XCFrameworks containing Swift code. When you mix Objective-C static libraries (React Native) with Swift dynamic frameworks (Ad Adapters) inside a static linkage environment, the linker often fails to find the Swift Standard Library symbols or duplicates symbols across dependencies.
Furthermore, if your project does not explicitly enable library distribution settings, the compiled module interfaces (.swiftinterface) become incompatible with the Xcode version performing the build.
The iOS Fix: Configuring the Podfile
To fix this, we must instruct CocoaPods to handle framework linkage correctly and ensure that the Swift compiler flags allow for distribution.
Open your ios/Podfile and apply the following changes.
1. Enable Static Framework Linkage
Ensure you are using the static linkage strategy. This is standard for modern React Native but essential for XCFramework compatibility.
target 'YourAppName' do
config = use_native_modules!
# Force static linkage for stability with RN + Ad Networks
use_frameworks! :linkage => :static
# ... rest of your pods
end
2. The Installer Post-Install Hook
This is the critical step. We must iterate through the targets and ensure BUILD_LIBRARY_FOR_DISTRIBUTION is enabled. This resolves the Swift module stability issues that cause linker failures.
Add or merge this block at the bottom of your Podfile:
post_install do |installer|
# React Native standard post_install
react_native_post_install(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# 1. Fix Swift Version to prevent mismatch errors
config.build_settings['SWIFT_VERSION'] = '5.0'
# 2. Enable Library Distribution to fix linker symbol visibility
config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
# 3. Force IPAD support if Appodeal requires it (optional but recommended)
config.build_settings['TARGETED_DEVICE_FAMILY'] = '1,2'
# 4. Handle "linker command failed" due to empty Swift files in some pods
# This flag prevents errors when a Pod has no source files but is linked
config.build_settings['OTHER_LDFLAGS'] ||= ['$(inherited)']
end
end
end
3. Clean and Rebuild
Linker errors cache aggressively. You must perform a deep clean. Run this from your project root:
cd ios
rm -rf Pods
rm -rf Podfile.lock
pod install
cd ..
rm -rf ~/Library/Developer/Xcode/DerivedData
The Android Root Cause: Kotlin 2.x Binary Incompatibility
Android Studio and the Android Gradle Plugin (AGP) are pushing aggressively toward Kotlin 2.0. However, react-native-appodeal (and many other community packages) are often compiled against Kotlin 1.8 or 1.9.
Kotlin metadata is stored in the binaries. If your project uses the K2 compiler (Kotlin 2.0+) but imports a library built with an older compiler that didn't expose compatible metadata, the build fails with: Class 'com.appodeal.ads...' was compiled with an incompatible version of Kotlin.
The Android Fix: Enforcing Kotlin Version Alignment
We cannot easily recompile the pre-built Appodeal AARs. Instead, we must align our project environment to accommodate the library's expected Kotlin version or force a specific resolution strategy.
1. Pin Kotlin Version in Root Gradle
In your android/build.gradle (Project Level), you likely define kotlinVersion. If you are on 2.0 and failing, downgrade to the most stable 1.9 release compatible with React Native.
// android/build.gradle
buildscript {
ext {
// Kotlin 1.9.24 is the sweet spot for React Native 0.74+
// and current ad network SDKs as of late 2024.
kotlinVersion = "1.9.24"
// Ensure other versions are standard
buildToolsVersion = "34.0.0"
minSdkVersion = 23
compileSdkVersion = 34
targetSdkVersion = 34
ndkVersion = "26.1.10909125"
}
dependencies {
// Ensure the classpath matches the version above
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
classpath("com.android.tools.build:gradle:8.3.0")
}
}
2. Force Resolution Strategy
If you absolutely must use Kotlin 2.x for other dependencies, you will encounter transitive dependency conflicts. You can force the Kotlin Standard Library (stdlib) to a single version across all modules.
Add this to android/build.gradle inside the allprojects block:
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://artifactory.appodeal.com/appodeal' }
}
// Force strict resolution to prevent version mismatch crashes
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
if (requested.group == 'org.jetbrains.kotlin' && requested.name.startsWith('kotlin-stdlib')) {
// Force all modules to use the version defined in ext
details.useVersion rootProject.ext.kotlinVersion
}
}
}
}
3. Disable Strict Kotlin Checks (The "Escape Hatch")
If the build still fails due to "wobbly" binaries (warnings treated as errors), you can suppress the compatibility checks in your app-level config.
In android/app/build.gradle:
android {
// ... setup
kotlinOptions {
jvmTarget = "1.8"
// Prevent build failure on 'incompatible' binaries
freeCompilerArgs += [
"-Xskip-metadata-version-check",
"-Xjvm-default=all"
]
}
}
Deep Dive: Why use_frameworks! Matters
Historically, React Native relied on header search paths and static libraries (.a files). Swift, however, relies on Modules.
When you add react-native-appodeal, it brings in the Appodeal SDK, which routes into multiple ad networks (Meta Audience Network, AdMob, BidMachine). Some of these are distributed only as compiled Swift XCFrameworks.
If you do not use use_frameworks! :linkage => :static, CocoaPods tries to wrap these Swift frameworks into a generated Objective-C header. This bridge is fragile. By forcing static linkage, you allow the React Native static library to merge with the Ad Network static frameworks at the linker level, assuming the architecture (arm64/x86_64) matches.
The linker command failed error often happens because the Linker sees a symbol in the Swift framework (e.g., _OBJC_CLASS_$_Appodeal) but expects it in a format compliant with the old Objective-C runtime. Enabling BUILD_LIBRARY_FOR_DISTRIBUTION forces the compiler to generate Module Interfaces that are resilient to these runtime discrepancies.
Edge Cases
The "Duplicate Symbol" Error
If your linker error specifically mentions duplicate symbol, it usually means two different libraries are including the same dependency (e.g., google-mobile-ads-sdk).
Fix: Check your package.json. If you have react-native-admob installed alongside react-native-appodeal, remove the standalone AdMob library. Appodeal includes the Google adapter transitively.
Architecture Mismatches (M1/M2/M3 Macs)
If the linker fails specifically on the iOS Simulator with undefined symbol for architecture arm64, your project is trying to run the Simulator (which is arm64 on Apple Silicon) but the ad network SDK only includes x86_64 for simulators.
Fix: Add this to your Podfile post_install hook:
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# Exclude arm64 for simulator builds to force Rosetta translation
# ONLY do this if the SDK is old and lacks arm64 simulator slices
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
end
end
Conclusion
Integrating Ad Mediation in React Native is a battle against the build system. The linker command failed error on iOS and Kotlin versioning issues on Android are rarely about your code logic—they are about ABI (Application Binary Interface) compatibility.
By enforcing static linkage in CocoaPods and strictly aligning the kotlin-stdlib in Gradle, you eliminate the ambiguity that causes these compilers to choke. Once the build passes, the runtime integration is straightforward.