Last active
April 29, 2023 16:11
-
-
Save atereshkov/df107336a076d5f34bc4b2a9eb6f9ad5 to your computer and use it in GitHub Desktop.
Xcode 10 Fat framework - How to create universal binary iOS framework (+ Cocoapods podspec)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If you've got something like this: | |
file was built for arm64 which is not the architecture being linked (x86_64): | |
Than this gist is for you. | |
Let's say you have already developed the framework itself and just want to build the binary | |
and distribute it through the Cocoapods. | |
1. Make sure your framework "Deployment Target" is equal to all the dependencies from your podspec file. | |
2. In your framework target -> "Build Settings" -> "Skip install" set to No | |
3. In build schemes drop down choose your framework -> "Edit scheme" -> "Archive" -> "Post-actions" -> | |
Click "+" sign | |
4. From the "Provide build settings from" drop down choose your target. | |
5. Paste the script below: | |
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}" | |
#mkdir -p "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework" | |
echo "Building for iPhoneSimulator" | |
xcodebuild -workspace "${WORKSPACE_PATH}" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone XS' 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}/" "${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/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule/." | |
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then | |
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/${TARGET_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}" | |
echo "Combining executables end" | |
# Step 4. Create universal binaries for embedded frameworks | |
for SUB_FRAMEWORK in $( ls "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks" ); do | |
BINARY_NAME="${SUB_FRAMEWORK%.*}" | |
echo "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" | |
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SUB_FRAMEWORK}/${BINARY_NAME}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${TARGET_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}" "${PROJECT_DIR}" | |
open "${PROJECT_DIR}" | |
fi | |
6. Tick "Shared" checkbox at the very bottom. | |
7. !!! Select real device from the devices drop down (let's say your iPhone). Run "Product" -> "Archive" | |
8. Wait for the Organaizer window, then wait for a while and if everything is fine | |
the project root will be opened in the finder. | |
9. You'll have MyKit.framework file in the project root folder. | |
10. You can open MyKit.framework, navigate to "Modules" -> "MyKit.swiftmodule" and make sure you have all the | |
architectures (In my case for deployment target 10 there are 4 architectures: arm, arm64, i386, x86_64) | |
====== | |
In order to make the pod availiable through the Cocoapods, make the default pod setup as for usual pods. | |
Then make sure you have copied the .framework file to the (let's say) "Framework" folder. | |
You'll have .git folder, "Framework" folder, "LICENSE" file, "MyKit.podspec" files in the pod folder. | |
Your podspec file should looks similar to this: | |
Pod::Spec.new do |s| | |
s.name = "MyKit" | |
s.version = "1.0.0" | |
s.summary = "MyKit for iOS" | |
s.description = <<-DESC | |
MyKit for iOS. | |
DESC | |
s.homepage = "https://github.com/atereshkov/mykit" | |
s.license = "MIT" | |
s.author = "Alexander Tereshkov" | |
s.platform = :ios, "10.0" | |
s.source = { :git => "https://github.com/atereshkov/mykit.git", :tag => "#{s.version}" } | |
s.frameworks = 'UIKit', 'WebKit' | |
s.dependency 'Alamofire', '~> 4.8' | |
s.ios.vendored_frameworks = "Framework/MyKit.framework" | |
s.swift_version = "4.2" | |
end | |
====== | |
Then run "pod lib init" from the pod root folder | |
-> MyKit (1.0.0) | |
- NOTE | xcodebuild: note: Using new build system | |
- NOTE | [iOS] xcodebuild: note: Planning build | |
- NOTE | [iOS] xcodebuild: note: Constructing build description | |
- NOTE | [iOS] xcodebuild: warning: Skipping code signing because the target does not have an Info.plist file. (in target 'App') | |
And if you got the "MyKit passed validation." phrase, then everything is fine. | |
Just relax and continue with the default procedure of pod publication through the Cocoapods. | |
https://guides.cocoapods.org/making/making-a-cocoapod.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Pod::Spec.new do |s| | |
s.name = "MyKit" | |
s.version = "1.0.0" | |
s.summary = "MyKit for iOS" | |
s.description = <<-DESC | |
MyKit for iOS. | |
DESC | |
s.homepage = "https://github.com/atereshkov/mykit" | |
s.license = "MIT" | |
s.author = "Alexander Tereshkov" | |
s.platform = :ios, "10.0" | |
s.source = { :git => "https://github.com/atereshkov/mykit.git", :tag => "#{s.version}" } | |
s.frameworks = 'UIKit', 'WebKit' | |
s.dependency 'Alamofire', '~> 4.8' | |
s.ios.vendored_frameworks = "Framework/MyKit.framework" | |
s.swift_version = "4.2" | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}" | |
#mkdir -p "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework" | |
echo "Building for iPhoneSimulator" | |
xcodebuild -workspace "${WORKSPACE_PATH}" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone XS' 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}/" "${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/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule/." | |
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then | |
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/${TARGET_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}" | |
echo "Combining executables end" | |
# Step 4. Create universal binaries for embedded frameworks | |
for SUB_FRAMEWORK in $( ls "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks" ); do | |
BINARY_NAME="${SUB_FRAMEWORK%.*}" | |
echo "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" | |
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SUB_FRAMEWORK}/${BINARY_NAME}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${TARGET_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}" "${PROJECT_DIR}" | |
open "${PROJECT_DIR}" | |
fi |
hey thanks for your script, but I am having trouble using it with my framework which has a dependency. like I can not go to my framework project and click archive as the dependency is not added here in my project but marked as s.dependancy in the pod spec and thus client has to add it. so archive fails. can you help me with shipping the fat framework through the cocoa pod with dependencies? thanks in advance for your kind reply. basically, the dependency I added was app auth and it was built in objective c. and i couldn't find fat library for it and i added only the device framework, so once the archive has done i could see only arm64 related modules but not simulator one.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is the right version if product_name differs from target_name: