Skip to content

Instantly share code, notes, and snippets.

@sundeepgupta
Last active September 16, 2024 07:56
Show Gist options
  • 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}"
@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