Skip to content

Instantly share code, notes, and snippets.

@winadiw
Forked from atereshkov/podspec-example
Created March 14, 2019 11:54
Show Gist options
  • Save winadiw/cc025d43e19b948414035e480ae44062 to your computer and use it in GitHub Desktop.
Save winadiw/cc025d43e19b948414035e480ae44062 to your computer and use it in GitHub Desktop.
Xcode 10 Fat framework - How to create universal binary iOS framework (+ Cocoapods podspec)
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
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
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment