Exclude frameworks from xcframework archive - ios

I am using Xcode 13.4.1
I use the below code to compile and create xcframework
compile "clean archive -workspace '$input_path' -scheme '$scheme_name' -sdk $sdk_ios OBJROOT=Frameworks/build/iOS"
xcodebuild -create-xcframework \
-framework $source_path/$folder_name/Frameworks/build/iOS/UninstalledProducts/iphoneos/$output_name.framework \
I made some of the linked libraries to "do not embed" and "optional" framework in link binary with libraries
but I don't know why, the frameworks are added on to the xcframework in the folder frameworks. I don't know what else to do.
The weird thing is, this only happens on my Mac
I did change the setting of framework to "do not embed" and in build phases, Link binary with Libraries, I set the libraries to "optional"

Related

Build XCFramework with pod dependency

same question asked here : XCFramework with Pods Dependencies
but in my case i can't have it as cocoapod, i need it to be XCFramework
Our goal is to create a framework that hides our internal code and provide SDK to our customers. We have thought of creating XCFramework which fulfills our requirement. Umbrella framework is also suggested over the internet but mostly suggested to avoid that approach. Our Framework is dependent on some third-party libraries which we are using via Pods.
Issue: XCFramework does not compile pods framework. We got an error like "Xyz(Pod) module not found". Even if we add pods from the client-side it does not work.
Code to create XCFramework is as bellow
Create an archive for iOS platform
xcodebuild archive -workspace ABC.xcworkspace
-scheme ABC
-sdk iphoneos
-archivePath "./archives/ios_devices.xcarchive"
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
SKIP_INSTALL=NO
Create an archive for iOS-Simulator platform
xcodebuild archive -workspace ABC.xcworkspace
-scheme ABC
-sdk iphonesimulator
-archivePath "./archives/ios_simulators.xcarchive"
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
SKIP_INSTALL=NO
Create an XCFramework from Archives
xcodebuild -create-xcframework
-framework ./archives/ios_devices.xcarchive/Products/Library/Frameworks/ABC.framework
-framework ./archives/ios_simulators.xcarchive/Products/Library/Frameworks/ABC.framework
-output build/ABC.xcframework
We got ABC XCFramework successfully but dependencies are not included in XCFramework. Any solution for this? or Is there any way where we can set framework search path to client-side? or Any alternate approach?
Try Adding -workspace yourworkspace.xcworkspace.
pod dependencies are part of .xcworkpspace. so i guess you require workspace to be included.

How to build a release version of an iOS framework in Xcode?

Let's say I do the following:
Open Xcode 7
File | New | Project | Cocoa Touch Framework
Create "TestFramework" with the Swift language
Create a file Hello.swift with public func hello() { print("Hello") }.
From here, I can build a debug build of the framework (inside the Debug-iphoneos folder), but I cannot figure out how to build the release version of the framework (inside Release-iphoneos). I thought Archive might do it, but it doesn't. Pointers please?
To get a release build, you need to change your scheme settings:
Alternatively, create a new scheme for release builds.
Ensure you have a device selected. Not the simulator.
Build your project and you should see that it gets added to this location:
(Click the arrow to navigate there in finder)
And after drilling down, you should be able to find the release folder with your release framework inside.
This works for me:
Select your framework target then click Product -> Archive. If organizer window does not pop up after successful build of your framework then go to "Build Settings" of your framework target, look for the option "Skip Install" and change it to "No" (and after that Archive again).
An alternative to building a framework via the Xcode IDE is to build it from the command line.
You can produce a release build of your framework for iphoneos devices with the following command:
xcodebuild -workspace TestSDK.xcworkspace -scheme TestSDK -configuration Release -sdk iphoneos
You can change the value of the -configuration argument from Release to Debug in order to produce a debug build, or change the value of the -sdk argument from iphoneos to iphonesimulator in order to produce a build for Simulator devices.
Note that you may need to provide the -project argument instead of -workspace if your target is part of an Xcode project only and not part of an Xcode workspace. Run the xcodebuild -help command for the full list of xcodebuild options.
If you prefer to archive, you can do that from the command line also, as follows:
xcodebuild archive -workspace TestSDK.xcworkspace -scheme TestSDK -configuration Release -sdk iphoneos -archivePath "TestSDK_Release_iphoneos.xcarchive" SKIP_INSTALL=NO
Note that you can specify SKIP_INSTALL=NO as part of your project or target's Build Settings instead if you prefer.
Lastly, if you want to join up your iphoneos and iphonesimulator builds into a single binary, you can do that with the xcodebuild -create-xcframework command as follows:
xcodebuild -create-xcframework \
-framework "TestSDK_Release_iphoneos.xcarchive/Products/Library/Frameworks/TestSDK.framework" \
-framework "TestSDK_Release_iphonesimulator.xcarchive/Products/Library/Frameworks/TestSDK.framework" \
-output "TestSDK.xcframework"
See here for the official guide to creating an XCFramework.
When you add the framework to your other Xcode project then you have to add "$(BUILT_PRODUCTS_DIR)" to Build Settings -> Framework Search Paths.
This will create Debug when you run project (with Debug) and will create Release version when you archive project.
The archive doesn't will create Release version under Products dir but will create Release in "Intermediates.noindex" folder.

How do I xcodebuild a static library with Bitcode enabled?

Xcode 7 introduces Bitcode, which is some sort of LLVM intermediate binary that means Apple's servers can recompile my app for different architectures without my involvement.
At Lookback, I distribute a static archive framework with our library. It seems that when you build with anything but a "Build & Archive", bitcode is not actually emitted into my library, and anyone who links with my library in their app and tries to do a Build & Archive with Bitcode enabled will get one of two warnings:
ld: 'Lookback(Lookback.o)' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. (if lib is built with Xcode 6)
ld: warning: full bitcode bundle could not be generated because 'Lookback(Lookback.o)' was built only with bitcode marker. The library must be generated from Xcode archive build with bitcode enabled (Xcode setting ENABLE_BITCODE) (if lib is built with Xcode 7 with a normal xcodebuild)
I have a build script that builds a device+simulator universal binary, so I can't use Build & Archive, but rather, I run xcodebuild from commandline from my script. How can I make xcodebuild generate a proper bitcode-enabled library?
Bitcode is a compile-time feature (not a link-time feature) which means that every .o file should contain an extra section called __bitcode when built with bitcode. You can confirm whether your binary is bitcode-compatible by running otool -l (my .o or .a file) | grep __LLVM.
When you build normally, Xcode adds the build flag -fembed-bitcode-marker to any clang invocation. This seems to be some sort of 'this is where bitcode would go, if bitcode was enabled' thing, and doesn't actually enable bitcode.
When you "Build & Archive", this flag is replaced by -fembed-bitcode, which really does build a Bitcode-enabled binary.
There seems to be two ways to make xcodebuild use -fembed-bitcode:
Use the 'archive' action, as in xcodebuild -target LookbackSDK archive instead of xcodebuild -target LookbackSDK build. This has the side-effect of putting binaries in your Xcode Organizer instead of the build/ folder, though you can work around that by using -exportArchive -archivePath ./build (thanks #JensAyton)
Force usage of the flag by adding Other C Flags with OTHER_CFLAGS="-fembed-bitcode". Your xcodebuild invocation would look something like xcodebuild OTHER_CFLAGS="-fembed-bitcode" -target LookbackSDK build.
The latter is what I chose so that I don't have to change my build system, but it will generate warnings for every file, since now both -fembed-bitcode-marker and -fembed-bitcode are sent to clang. Luckilly the latter wins, generating a Bitcode-enabled library!
Resources
Apple DevForums: Bitcode and Assembly?
SO: iOS library to BitCode
With Xcode 8, I couldn't get OTHER_CFLAGS="-fembed-bitcode" to work. I kept running into something along the lines of was built without full bitcode. All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build when I tried to create an Archive build of an app containing my static framework.
What I was really looking for was this:
BITCODE_GENERATION_MODE=bitcode
I'm actually using a Run Script inside of an aggregate target, the full xcodebuild line looks like this (just for reference):
xcodebuild BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
Once you add bitcode support for the static lib, it won't be compatible with Xcode 6. The app won't archive.
I would like to clearly mention the setting for bitcode as #nevyn's answer confused me a little.
Go to Build settings, search for "custom compiler flags". Add -fembed-bitcode. This will build your lib with bitcode.
Select project
On Build Settings -> Other C flags, set Debug to -fembed-bitcode-marker and Release to -fembed-bitcode
On Build Settings, click on the + sign at the top to add a user-defined build setting with the name BITCODE_GENERATION_MODE, and set Debug to marker, Release to bitcode
Edit schema as Release
Then click the desired library. A file and get the build path.
Get the library form Release folder.

How to export "fat" Cocoa Touch Framework (for Simulator and Device)?

With Xcode 6 we get ability to create own Dynamic Cocoa Frameworks.
Because of:
Simulator still use 32-bit library
beginning June 1, 2015 app updates submitted to the App Store must include 64-bit support and be built with the iOS 8 SDK (developer.apple.com)
We have to make fat library to run project on devices and simulators. i.e. support both 32 and 64 bit in Frameworks.
But I didn't find any manuals, how to export universal fat Framework for future integration with other projects (and share this library with someone).
Here is my steps to reproduce:
Set ONLY_ACTIVE_ARCH=NO in the Build Settings
Add support armv7 armv7s arm64 i386 x86_64 to Architectures (for sure)
Build Framework and open it in Finder:
Add this framework to another project
Actual result:
But in the end I still have problem with running project with this framework on devices and simulator at once.
if I take framework from Debug-iphoneos folder - it works on devices
and gets error on simulators: ld: symbol(s) not found for architecture i386
xcrun lipo -info CoreActionSheetPicker
Architectures in the fat file: CoreActionSheetPicker are: armv7 armv7s arm64
if I take framework from Debug-iphonesimulator folder - it works on simulators. and I have error on device: ld: symbol(s) not found for architecture arm64
xcrun lipo -info CoreActionSheetPicker
Architectures in the fat file: CoreActionSheetPicker are: i386 x86_64
So, how to create a dynamic framework that works on devices and simulators?
This answer related to Xcode 6 iOS Creating a Cocoa Touch Framework - Architectures issues but it's not duplicate.
Update:
I found a "dirty hack" for this case. See my answer below.
If someone knows more convenient way - please, let me know!
The actuality of this answer is: July 2015. It is most likely that things will change.
TLDR;
Currently Xcode does not have tools for automatic export of universal fat framework so developer must resort to manual usage of lipo tool. Also according to this radar before submission to AppStore developer who is framework's consumer also must use lipo to strip off simulator slices from a framework.
Longer answer follows
I did similar research in the topic (the link at the bottom of the answer).
I had not found any official documentation about distribution of so my research was based on exploration of Apple Developer Forums, Carthage and Realm projects and my own experiments with xcodebuild, lipo, codesign tools.
Here's long quote (with a bit of markup from me) from Apple Developer Forums thread Exporting app with embedded framework:
What is the proper way to export a framework from a framework project?
Currently the only way is exactly what you have done:
Build the target for both simulator and iOS device.
Navigate to Xcode's DerivedData folder for that project and lipo the two binaries together into one single framework. However, when you build the framework target in Xcode, make sure to adjust the target setting 'Build Active Architecture Only' to 'NO'. This will allow Xcode to build the target for multiple binarty types (arm64, armv7, etc). This would be why it works from Xcode but not as a standalone binary.
Also you'll want to make sure the scheme is set to a Release build and build the framework target against release.
If you are still getting a library not loaded error, check the code slices in the framework.
Use lipo -info MyFramworkBinary and examine the result.
lipo -info MyFrameworkBinary
Result is i386 x86_64 armv7 arm64
Modern universal frameworks will include 4 slices, but could include more: i386 x86_64 armv7 arm64
If you don't see at least this 4 it coud be because of the Build Active Architecture setting.
This describes process pretty much the same as #skywinder did it in his answer.
This is how Carthage uses lipo and Realm uses lipo.
IMPORTANT DETAIL
There is radar: Xcode 6.1.1 & 6.2: iOS frameworks containing simulator slices can't be submitted to the App Store and a long discussion around it on Realm#1163 and Carthage#188 which ended in special workaround:
before submission to AppStore iOS framework binaries must be stripped off back from simulator slices
Carthage has special code: CopyFrameworks and corresponding piece of documentation:
This script works around an App Store submission bug triggered by universal binaries.
Realm has special script: strip-frameworks.sh and corresponding piece of documentation:
This step is required to work around an App Store submission bug when archiving universal binaries.
Also there is good article: Stripping Unwanted Architectures From Dynamic Libraries In Xcode.
I myself used Realm's strip-frameworks.sh which worked for me perfectly without any modifications though of course anyone is free to write a one from scratch.
The link to my topic which I recommend to read because it contains another aspect of this question: code signing - Creating iOS/OSX Frameworks: is it necessary to codesign them before distributing to other developers?
This is not so clear solution, but there is only way, that I find:
Set ONLY_ACTIVE_ARCH=NO in the Build Settings
Build library for simulator
Build library for device
Open in console Products folder for your framework (you can open it by open framework folder and cd .. from there)
Run this script from Products folder. It creates fat Framework in this folder. (or do it manually as explained below in 3. 4.)
Or:
Combine these 2 Frameworks using lipo by this script (replace YourFrameworkName to your Framework name)
lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
Replace with new binary one of the existing frameworks:
cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
Profit: ./YourFrameworkName.framework - is ready-to-use fat binary! You can import it to your project!
For project, that not in Workspaces:
You can also try to use this gist as described here. But it seems, that it not works for projects in workspaces.
#Stainlav answer was very helpful but what I did instead was to compile two versions of the framework (one for the device and one for simulator) and then added the following Run Script Phase to automatically copy the precompiled framework required for the running architecture
echo "Copying frameworks for architecture: $CURRENT_ARCH"
if [ "${CURRENT_ARCH}" = "x86_64" ] || [ "${CURRENT_ARCH}" = "i386" ]; then
cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi
This way I don't have use lipo to create a fat framework neither the Realm's strip-frameworks.sh to remove the unnecessary slices when submitting to the App Store.
basically for this i found very good solution. you just need to follow these simple steps.
Create a cocoa touch framework.
Set bitcode enabled to No.
Select your target and choose edit schemes. Select Run and choose Release from Info tab.
No other setting required.
Now build the framework for any simulator as simulator runs on x86 architecture.
Click on Products group in Project Navigator and find the .framework file.
Right click on it and click on Show in finder. Copy and paste it in any folder, I personally prefer the name 'simulator'.
Now build the framework for Generic iOS Device and follow the steps 6 through 9. Just rename the folder to 'device' instead of 'simulator'.
Copy the device .framework file and paste in any other directory. I prefer the immediate super directory of both.
So the directory structure now becomes:
Desktop
device
MyFramework.framework
simulator
MyFramework.framework
MyFramework.framework
Now open terminal and cd to the Desktop. Now start typing the following command:
lipo -create 'device/MyFramework.framework/MyFramework'
'simulator/MyFramework.framework/MyFramework' -output
'MyFramework.framework/MyFramework'
and that's it. Here we merge the simulator and device version of MyFramework binary present inside MyFramework.framework. We get a universal framework that builds for all the architectures including simulator and device.
My Answer cover the below points:
Make framework which works for both simulator and Device
How to export “fat” Cocoa Touch Framework (for Simulator and Device both)?
Undefined symbols for architecture x86_64
ld: symbol(s) not found for architecture x86_64
Steps 1: First build your frameworks with Simulator target
Steps 2: After success of simulator building process, now build for your framework with device target selection or Generic iOS Device selection
Step 3: Now select your framework target and for that Under "Build Phases" select "Add Run Script" and copy the below script code)
Step4: Now finally, build again and your framework is ready for both simulator and device compatibility. Hurray!!!!
[Note: We must have both compatible framework ready before final step4 (simulator and device architecture compatible, if not please follow above steps 1 and 2 correctly)
See the reference image:
Put below code into shell area:
#!/bin/sh
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
# Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
# 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_OUTPUTFOLDER}/${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_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
# Step 6. Convenience step to open the project's directory in Finder
open "${BUILD_DIR}/${CONFIGURATION}-universal"
I just want to update this great answer by #odm. Since Xcode 10, the CURRENT_ARCH variable doesn't reflect the build architecture anymore. So I changed the script to check the platform instead:
echo "Copying frameworks for platform: $PLATFORM_NAME"
rm -R "${SRCROOT}/Frameworks/Active"
if [ "${PLATFORM_NAME}" = "iphonesimulator" ]; then
cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi
I also added a line to clear the target directory before copying, because I noticed that additional files in subdirectories wouldn't be overwritten otherwise.

Xcode 6 and iOS Static Frameworks

A lot of iOS 3rd Party frameworks are built around custom frameworks, that you are going to integrate in your dependent app.
Before the new iOS Embedded Framework that only works in iOS8 (see my post here), there were two elegant solution for that (when you do not choose to create your scripts for a matter of time and knowledge on that)
The iOS Universal Framework by kstenerud
and
The iOS Framework by jverkoey
They both worked and were good solution when have to create a .framework to distribute a solution for your clients.
As soon as Xcode6 came out, I was not able to make the first to work due to compilation errors of different kind (see here for details).
So I moved to the iOS Framework, and with my surprise it still works on XCode6.
So this advice is for the ones that are struggling with iOS Universal Framework and do not find any solution to make it works on Xcode6.
Since as depicted in 1 there is now way for Apple to accept Embedded Frameworks if your deployment target is >= iOS7, this is the best custom solution at the moment, and a fix for the iOS Universal Framework could help as well.
All the issues I have found on it are depicted in 5.
Here step to create static cocoa touch framework in Xcode 6.
Open Xcode and create a new static framework project by clicking File\New\Project and selecting iOS\Framework and Library\Cocoa Touch framework.
You can provide your framework name and save the project to an empty directory.
Automatically umbrella header created for our framework. In this header , you should import all the public headers of our framework using the statements like #import
A static framework project is made up of header files and implementation files, which are compiled to make the framework itself. You can create the class by using the Cocoa Touch class.
Verifying Your Build Settings Go to the Build Settings of your project Target and confirm or set the “Architectures” to “Standard Architectures.” These are arm64 and armv7, and are likely the default. As well as, Also we need to set the few architectures in setting, because iOS apps need to run on many different architectures.
armv7: Used in the oldest iOS 7-supporting devices armv7s: As used in iPhone 5 and 5C arm64: For the 64-bit ARM processor in iPhone 5S i386: For the 32-bit simulator x86_64: Used in 64-bit simulator
This means that builds are as fast as they can be. When you archive an app or build in release mode, then Xcode will build for all ARM architectures, thus allowing the app to run on most devices.
Mach-O setting: Static library:
Final Build project:
Aggregate target to combine the device and simulator framework by using lipo, you can add the below script to your aggregate target.
FRAMEWORK_NAME="${PROJECT_NAME}"
SIMULATOR_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${FRAMEWORK_NAME}.framework"
DEVICE_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphoneos/${FRAMEWORK_NAME}.framework"
UNIVERSAL_LIBRARY_DIR="${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal"
FRAMEWORK="${UNIVERSAL_LIBRARY_DIR}/${FRAMEWORK_NAME}.framework"
xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphonesimulator -arch i386 -arch x86_64 -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator | echo
xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphoneos -arch arm64 -arch armv7 -arch armv7s -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos | echo
rm -rf "${UNIVERSAL_LIBRARY_DIR}"
mkdir "${UNIVERSAL_LIBRARY_DIR}"
mkdir "${FRAMEWORK}"
cp -r "${DEVICE_LIBRARY_PATH}/." "${FRAMEWORK}"
lipo "${SIMULATOR_LIBRARY_PATH}/${FRAMEWORK_NAME}""${DEVICE_LIBRARY_PATH}/${FRAMEWORK_NAME}" -create -output"${FRAMEWORK}/${FRAMEWORK_NAME}" | echo
You can use Xcode 6 Universal Framework in iOS 7, - see my answer here

Resources