Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
This run script will build the iphoneos and iphonesimulator schemes and then combine them into a single framework using the lipo tool (including all the Swift module architectures). This version works with Cocoapods, merging simulator and device architectures for intermediate libraries as well. To use this script, go to Product > Scheme > Edit S…
exec > /tmp/${PROJECT_NAME}_archive.log 2>&1
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
if [ "true" == ${ALREADYINVOKED:-false} ]
then
echo "RECURSION: Detected, stopping"
else
export ALREADYINVOKED="true"
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
echo "Building for iPhoneSimulator"
xcodebuild -workspace "${WORKSPACE_PATH}" -scheme "${SCHEME_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6' ONLY_ACTIVE_ARCH=NO ARCHS='i386 x86_64' BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" ENABLE_BITCODE=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE=bitcode clean build
# Step 1. Copy the framework structure (from iphoneos build) to the universal folder
echo "Copying to output folder"
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 2. 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_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
fi
# Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory
echo "Combining executables"
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${EXECUTABLE_PATH}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${EXECUTABLE_PATH}"
# Step 4. Create universal binaries for embedded frameworks
for SUB_FRAMEWORK in $( ls "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Frameworks" ); do
BINARY_NAME="${SUB_FRAMEWORK%.*}"
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SUB_FRAMEWORK}/${BINARY_NAME}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${PROJECT_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}"
done
# Step 5. Convenience step to copy the framework to the project's directory
echo "Copying to project dir"
yes | cp -Rf "${UNIVERSAL_OUTPUTFOLDER}/${FULL_PRODUCT_NAME}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}"
fi
@atsepkov

This comment has been minimized.

Copy link
Owner Author

@atsepkov atsepkov commented May 18, 2016

Unlike other versions of the script intended to run from command line or aggregate target, this script will only build the simulator portion, the device portion should be built by Xcode automatically. As a result, rather than a new build method for the fat binary like most other solutions suggest, this leverages the existing build method and just adds a few extra steps to it.

To use this gist in Xcode for your project, do the following:

  • Select the framework you're interested in building
  • Go to Product > Scheme (make sure correct scheme is selected) > Edit Scheme
  • Go to Archive > Post-actions and add (+) a new Run Script stage
  • Make sure shell type is set to /bin/sh and copy the above script into the text area
  • To build, go to Product > Archive and you should see the organizer window after the build finishes, your regular binary will now become a fat binary.
@mikefogg

This comment has been minimized.

Copy link

@mikefogg mikefogg commented Aug 3, 2016

@atsepkov Thanks for putting this up here! I had question I hope isn't too complicated but, must be missing something. I follow the steps above and am getting a successful build!

The issue I'm having is that nothing is showing up at all in the Organizer at all. "Reveal Archive in Organizer" is checked, yet organizer never opens (and opening it manually just shows that nothing is there).

Any thoughts on what I could be missing?

@mikefogg

This comment has been minimized.

Copy link

@mikefogg mikefogg commented Aug 3, 2016

@atsepkov Ahh it seems like (looking at the log) the framework build is failing when building for simulator (can't find the the various cocoapods). Did you run into this error at all when you were building yours? This is what happens (for example) when using the SCLAlertView pod.

/sites/MySite/Manager.h:17:9: fatal error: 'SCLAlertView.h' file not found
#import "SCLAlertView.h"

This project does build correctly when I just build the framework for simulator.

@samjarman

This comment has been minimized.

Copy link

@samjarman samjarman commented Feb 9, 2017

What would this look like for an objective-c project? No swift modules

@christ776

This comment has been minimized.

Copy link

@christ776 christ776 commented May 17, 2017

same here, I'm getting a successful build, but nothing shows up on the Organizer.

@eciftcioglu

This comment has been minimized.

Copy link

@eciftcioglu eciftcioglu commented Jul 21, 2017

Hello, I have a problem that unless Bitcode is set to NO the script does not generate the binaries for the simulator and if the bitcode is enabled when I add my framework to a test app I have an error that says bitcode was not enabled by the creator of this framework.

@sindhuiOS

This comment has been minimized.

Copy link

@sindhuiOS sindhuiOS commented Oct 18, 2017

try to give answer to my STO question about creating framework from terminal.

https://stackoverflow.com/q/46808851/6285383

@donly

This comment has been minimized.

Copy link

@donly donly commented Oct 30, 2017

no work in Xcode 9

@sachinvas

This comment has been minimized.

Copy link

@sachinvas sachinvas commented Nov 16, 2017

The skip install is a vital step in this script, if its YES, the ${ARCHIVE_PRODUCTS_PATH} is /Library, and if it is NO, this is set to proper folder to get the framework. Checked on Xcode 9.

Kudos to author.

@AaronLBratcher

This comment has been minimized.

Copy link

@AaronLBratcher AaronLBratcher commented Oct 9, 2018

Can this be updated for Xcode 10?

@oleghnidets

This comment has been minimized.

Copy link

@oleghnidets oleghnidets commented Nov 22, 2018

Doesn't work in Xcode 10.

@dmdeller

This comment has been minimized.

Copy link

@dmdeller dmdeller commented Dec 4, 2018

This does work for me in Xcode 9.4.1 but not Xcode 10. The cause of the issue seems to be a new build system in Xcode 10: https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes/build_system_release_notes_for_xcode_10

As a temporary workaround in Xcode 10, you can set the build system to 'Legacy Build System' in the File menu > Workspace Settings. I would not expect this setting to remain available forever.

@GiuseppePiscopo

This comment has been minimized.

Copy link

@GiuseppePiscopo GiuseppePiscopo commented May 29, 2019

@dmdeller there's also an Apple Dev Forum thread on that topic, with this comment down below showing an automated solution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.