Skip to content

Instantly share code, notes, and snippets.

@sundeepgupta
Last active September 19, 2023 08:11
Show Gist options
  • Star 33 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save sundeepgupta/3ad9c6106e2cd9f51c68cf9f475191fa to your computer and use it in GitHub Desktop.
Save sundeepgupta/3ad9c6106e2cd9f51c68cf9f475191fa to your computer and use it in GitHub Desktop.
Script to create a universal or "fat" binary for an iOS framework.
#!/bin/bash
# Adapted from http://stackoverflow.com/questions/24039470/xcode-6-ios-creating-a-cocoa-touch-framework-architectures-issues/26691080#26691080
# and https://gist.github.com/cromandini/1a9c4aeab27ca84f5d79
# Create a new aggregate target.
# For the automatically generated scheme, change its build config to "release".
# Ensure this target's "product name" build setting matches the framework's.
# Add a run script with `source "${PROJECT_DIR}/path_to_this_script`
UNIVERSAL_OUTPUT_DIR=${BUILD_DIR}/${CONFIGURATION}-universal
RELEASE_DIR=${PROJECT_DIR}/build
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUT_DIR}"
# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build -sdk iphoneos
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build -sdk iphonesimulator
# Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PRODUCT_NAME}.framework" "${UNIVERSAL_OUTPUT_DIR}/"
# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUT_DIR}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
fi
# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUT_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PRODUCT_NAME}.framework/${PRODUCT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"
# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUT_DIR}/${PRODUCT_NAME}.framework" "${RELEASE_DIR}"
# Step 6. Convenience step to open the project's directory in Finder
open "${RELEASE_DIR}"
@presscorp
Copy link

presscorp commented Dec 14, 2020

Doesn't work properly on Xcode 12.2, fails on step 4 (line #31): lipo error while creating library for both device and simulator
To fix that I suggest to to add EXCLUDED_ARCHS="arm64" to line #19

@colejd
Copy link

colejd commented Jan 5, 2021

@presscorp I don't recommend doing that, or you won't be able to run your library on 64-bit iPhones or M1 Macs running the simulator. The issue you're having is that you're trying to make a universal binary that supports the same architecture twice (once for iphoneos and once for iphonesimulator), which isn't allowed. I'm guessing that you're using an M1 Mac, so you're trying to make a universal binary for arm64 on iphoneos and arm64 on iphonesimulator (your computer).

XCFrameworks are a new framework format meant to solve this problem. Replace the lipo command at line 31 with this:

xcodebuild -create-xcframework -output "${UNIVERSAL_OUTPUT_DIR}/${PRODUCT_NAME}.xcframework" \
  -framework "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PRODUCT_NAME}.framework" \
  -framework "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PRODUCT_NAME}.framework"

Then you can use the generated xcframework pretty much exactly like you'd use a framework.

(You might to add BUILD_LIBRARY_FOR_DISTRIBUTION=YES on the xcodebuild commands on lines 18 and 19 if your library has Swift code, or the Swift modules won't be generated and my new code above will fail to find the module maps.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment