Resolving Invalid Executable Bitcode Error in Xcode 16
Problem Statement
When distributing iOS apps to TestFlight using Xcode 16, developers encounter the error:
Invalid Executable. The executable 'appname.app/Frameworks/hermes.framework/hermes' contains bitcode.
This occurs even when ENABLE_BITCODE
explicitly set to NO
for both the main project and CocoaPods dependencies. The issue primarily stems from precompiled frameworks (like Hermes.js engine) containing embedded bitcode that conflicts with Xcode 16's distribution requirements.
Common symptoms:
- Distribution pipeline fails during App Store submission
- Configuration flags like
ENABLE_BITCODE = NO
have no effect - Occurs after upgrading macOS/Xcode or integrating third-party frameworks
Solutions
1) Stripping Bitcode from Frameworks Using a Comprehensive Script
This automated solution scans your project for frameworks containing bitcode and strips them. Create an executable script in your /ios
directory:
#!/bin/bash
check_bitcode() {
local binary_path=$1
if otool -l "$binary_path" | grep -q __LLVM; then
echo "$binary_path contains bitcode."
echo "$binary_path" >> bitcode_frameworks.txt
else
echo "$binary_path does not contain bitcode."
fi
}
strip_bitcode() {
local binary_path=$1
local output_path="${binary_path}_stripped"
xcrun bitcode_strip "$binary_path" -r -o "$output_path"
echo "Stripped bitcode from $binary_path"
}
replace_framework() {
local original_path=$1
local stripped_path="${original_path}_stripped"
[ -f "$stripped_path" ] && mv "$stripped_path" "$original_path"
}
disable_bitcode_in_project() {
local xcodeproj_file=$(find . -name "*.xcodeproj" | head -n 1)
/usr/libexec/PlistBuddy -c "Set :buildSettings:ENABLE_BITCODE NO" "$xcodeproj_file/project.pbxproj"
xcodebuild clean -quiet
}
# Detect binary frameworks
rm -f bitcode_frameworks.txt
find . -name '*.framework' -type d | while read framework; do
binary_name=$(basename "$framework" .framework)
binary_path="$framework/$binary_name"
[ -f "$binary_path" ] && check_bitcode "$binary_path"
done
# Process frameworks with bitcode
[ -f bitcode_frameworks.txt ] && while read binary_path; do
strip_bitcode "$binary_path"
replace_framework "$binary_path"
done < bitcode_frameworks.txt
# Update project settings
disable_bitcode_in_project
echo "Bitcode removed successfully!"
To execute:
- Create file:
touch ios/handle_bitcode.sh
- Make executable:
sudo chmod +x ios/handle_bitcode.sh
- Run:
cd ios && ./handle_bitcode.sh
- Clean project: Delete
DerivedData
folder and rebuild
2) Stripping Specific Frameworks via CocoaPods Post-Install Hook
Add this to your Podfile
to target known problem frameworks during the pod installation phase:
post_install do |installer|
bitcode_strip_path = `xcrun --find bitcode_strip`.chop!
def strip_framework(bitcode_strip_path, path)
full_path = File.join(Dir.pwd, path)
command = "#{bitcode_strip_path} #{full_path} -r -o #{full_path}"
system(command)
end
frameworks = [
# Hermes paths
"Pods/hermes-engine/destroot/Library/Frameworks/macosx/hermes.framework/hermes",
"Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework/ios-arm64/hermes.framework/hermes",
"Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework/ios-arm64_x86_64-maccatalyst/hermes.framework/hermes"
]
frameworks.each { |path| strip_framework(bitcode_strip_path, path) }
end
To apply:
# Reset CocoaPods environment
pod deintegrate
pod cache clean --all
pod install --repo-update
# Rebuild project
npx cap sync ios
npx cap open ios
Framework Path Verification
Use this command to verify your framework paths:
find Pods -name Hermes -print
Adjust paths in the Podfile snippet if different from default
Explanation
Why this happens
- Precompiled frameworks (like Hermes) include bitcode regardless of project settings
- Xcode 16 strictly enforces bitcode removal in distributed binaries
- Setting
ENABLE_BITCODE = NO
prevents adding bitcode but doesn't remove existing bitcode
Why these solutions work
Script-based method:
- Automatically detects frameworks with bitcode
- Uses
bitcode_strip
tool to remove bitcode segments - Updates Xcode project settings to prevent future conflicts
CocoaPods hook:
- Processes frameworks immediately after installation
- Prevents bitcode from persisting in dependency build pipelines
- Avoids manual framework modifications
Critical notes
- Always clean build folders (
DerivedData
) after applying fixes - Verify binaries contain no bitcode with:bash
otool -l path/to/framework | grep __LLVM
- Update Hermes to latest version where available to prevent recurrence
Platform considerations
Both solutions require macOS with Xcode CLI tools. For React Native projects, ensure Hermes is updated to the latest version compatible with Xcode 16.