Skip to content

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:

bash
#!/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:

  1. Create file: touch ios/handle_bitcode.sh
  2. Make executable: sudo chmod +x ios/handle_bitcode.sh
  3. Run: cd ios && ./handle_bitcode.sh
  4. 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:

ruby
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:

bash
# 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:

bash
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

  1. Script-based method:

    • Automatically detects frameworks with bitcode
    • Uses bitcode_strip tool to remove bitcode segments
    • Updates Xcode project settings to prevent future conflicts
  2. 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.