I have a xcframework project which has imported a few Swift package manager dependencies. I am building this framework with the following commands to create a universal binary.
xcodebuild -project Proj.xcodeproj archive -scheme Proj -sdk iphoneos -configuration Release clean build -derivedDataPath archives/device BUILD_LIBRARY_FOR_DISTRIBUTION=YES SKIP_INSTALL=NO ONLY_ACTIVE_ARCH=NO
xcodebuild -project Proj.xcodeproj archive -scheme Proj -sdk iphonesimulator -configuration Release clean build -derivedDataPath archives/simulator BUILD_LIBRARY_FOR_DISTRIBUTION=YES SKIP_INSTALL=NO ONLY_ACTIVE_ARCH=NO
xcodebuild -create-xcframework -framework archives/device/Build/Products/Release-iphoneos/Proj.framework -framework archives/simulator/Build/Products/Release-iphonesimulator/Proj.framework -output universal/Proj.xcframework
Then I import this into my project. But I cannot resolve the modules installed in xcframework. It says module is not found. What I want to do here is to reuse the bundled SPM packages without importing them to the final project again. Build libraries for Distribution is set to Yes in xcframework Build Settings.
When I import the SPM modules in the project it self it says " Class axyz is implemented in both . One of the two will be used. Which one is undefined." This means the SPM packages seems to be imported but cannot be linked properly.
Answering my own question. Adding #_implementationOnly in each package import in the framework did the trick.
Related
I have written the below commands in a script file to create XCFrameworks for both iPhoneSimulator and iPhoneOS like this below:
xcodebuild archive -scheme "ProjectD" -archivePath "/Users/Ron/Archives/ProjectDiphoneSimulator.xcarchive" -sdk 'iphonesimulator' SKIP_INSTALL=NO
xcodebuild archive -scheme "ProjectD" -archivePath "/Users/Ron/Archives/ProjectDiphoneOS.xcarchive" -sdk 'iphoneos' SKIP_INSTALL=NO
xcodebuild -create-xcframework -framework "/Users/Ron/Archives/ProjectDiphoneSimulator.xcarchive/Products/Library/Frameworks/ProjectDiphoneSimulator.framework" -output "/Users/Ron/XCFrameworks/ProjectDiphoneSimulator.xcframework"
xcodebuild -create-xcframework -framework "/Users/Ron/Archives/ProjectDiphoneOS.xcarchive/Products/Library/Frameworks/ProjectDiphoneOS.framework" -output "/Users/Ron/XCFrameworks/ProjectDiphoneOS.xcframework"
I tried to use this in a project after dragging and dropping it onto the Embedded Frameworks and Libraries
When I run the project with a device as the target, it works fine. But when I run it on a simulator, it throws the below error:
While building for iOS Simulator, no library was found in /Users/Ron/XCFrameworks/ProjectD.xcframework
If I change the order of the archive and XCFramework creation like below:
xcodebuild archive -scheme "ProjectD" -archivePath "/Users/Ron/Archives/ProjectDiphoneOS.xcarchive" -sdk 'iphoneos' SKIP_INSTALL=NO
xcodebuild archive -scheme "ProjectD" -archivePath "/Users/Ron/Archives/ProjectDiphoneSimulator.xcarchive" -sdk 'iphonesimulator' SKIP_INSTALL=NO
xcodebuild -create-xcframework -framework "/Users/Ron/Archives/ProjectDiphoneOS.xcarchive/Products/Library/Frameworks/ProjectDiphoneOS.framework" -output "/Users/Ron/XCFrameworks/ProjectDiphoneOS.xcframework"
xcodebuild -create-xcframework -framework "/Users/Ron/Archives/ProjectDiphoneSimulator.xcarchive/Products/Library/Frameworks/ProjectDiphoneSimulator.framework" -output "/Users/Ron/XCFrameworks/ProjectDiphoneSimulator.xcframework"
It works for simulator and not on the device with the same error:
While building for iPhone, no library was found in /Users/Ron/XCFrameworks/ProjectD.xcframework
I have set the Build Libraries for Distribution to Yes as well. If I try to create them with different destinations(2 XCFrameworks for iphone and simulator) it is working. But that literally defeats the purpose of XCFrameworks.
Am I missing something or doing any of the steps wrong? Thanks for the answers in advance.
You need to create a single .xcframework file from both .framework. (that will include simulator and device architectures) To do that run a single xcodebuild -create-xcframework command and pass the 2 different .framework files:
xcodebuild
-create-xcframework
-framework "/Users/Ron/Archives/ProjectDiphoneOS.xcarchive/Products/Library/Frameworks/ProjectDiphoneOS.framework"
-framework "/Users/Ron/Archives/ProjectDiphoneSimulator.xcarchive/Products/Library/Frameworks/ProjectDiphoneSimulator.framework"
-output "/Users/Ron/XCFrameworks/ProjectDiphoneOS.xcframework"
When you instead run 2 different xcodebuild -create-xcframework commands you have as a result your .xcframework to include only the last .framework file you passed, which is for devices. That's why your project fails to run in simulator.
Have setup a sample on Github, I'm using Xcode v 9.4.1
https://github.com/iousin/TreeFramework
Basically I'm trying to build my framework to include bitcode in the x86_64 simulator build. I tried various settings and tried building on command line, xcodebuild refuses to include bitcode in my simulator build, however it is very happy to include it in the arm64 (device) build.
All the following commands should be able to run from the same folder the above framework is checked out.
Following are the commands I've tried to build the above framework:
xcodebuild ENABLE_BITCODE[sdk=iphone*]=YES BITCODE_GENERATION_MODE=bitcode DYLIB_COMPATIBILITY_VERSION=1 -sdk iphonesimulator -configuration Release -target TreeFramework clean build
xcodebuild OTHER_CFLAGS="-fembed-bitcode" ENABLE_BITCODE[sdk=iphone*]=YES BITCODE_GENERATION_MODE=bitcode -sdk iphonesimulator -configuration Release -target TreeFramework clean build
xcodebuild OTHER_CFLAGS="-fembed-bitcode" ENABLE_BITCODE="YES" BITCODE_GENERATION_MODE="bitcode" -sdk iphonesimulator -configuration Release -target TreeFramework clean build
Ran the following to verify bitcode is emitted (it doesn't in this case).
otool -arch x86_64 -l build/Release-iphonesimulator/TreeFramework.framework/TreeFramework | grep LLVM
However when I build for a device, bitcode is included.
xcodebuild -sdk iphoneos -configuration Release -target TreeFramework clean build
Verify bitcode is emitted:
otool -arch arm64 -l build/Release-iphoneos/TreeFramework.framework/TreeFramework | grep LLVM
Appreciate any help.
I'm building a framework for my company. I have a workspace with two projects inside, the Cocoa Touch Framework and the Example app. I've a Run Script, that runs every time I build my framework, that builds simulator and device version and then lipo them to create fat framework. All of this is distributed via Cocoapods, so I've set the vendored_frameworks attribute on the podspec and so on. Everything installs correctly in other projects, except for one thing : it's impossible to archive projects because the framework does not contain bitcode. These are my xcodebuild command (in the run script) :
# Build both device and simulator versions for iOS
xcodebuild BITCODE_GENERATION_MODE=bitcode -project "${PROJECT_DIR}/${PROJECT_NAME}.xcodeproj" -derivedDataPath "${BUILD_DIR}" -scheme "${PROJECT_NAME}" -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone X' clean build
xcodebuild BITCODE_GENERATION_MODE=bitcode -project "${PROJECT_DIR}/${PROJECT_NAME}.xcodeproj" -derivedDataPath "${BUILD_DIR}" -scheme "${PROJECT_NAME}" -sdk iphoneos clean build
I set the build configuration to Release, so it does not use Debug. However, always the same problem : no way to archive an app with this framework inside.
Do you have any suggestion?
There are 2 options:
1.Try this to build your framework wth bitcode:
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
xcodebuild BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
2.Or you opt to disable bitcode in your application
I am creating Framework, I used aggregate target in that framework where we can write shell script to Build the framework for device as well as simulator based on script based on that i will have executable framework that can be imported in any project and we can use that class and method of framework.
But issue is that when we create executable framework with "schema device" then i can able to run on all device but if i make build using particular iOS5/iOS6 simulator then it will run only selected iOS5/iOS6 simulator in client project.
if i have created framework build using iOS5 simulator and if i run that framework in client project and i used iOS6 simulator it gives Undefined symbols for architecture x86_64.
I need shell script which support both architecture i386 and x86_64 means my framework should be executable for all device and all simulator in client project.
Any insights into this would be really helpful.
Create aggregate target and inside its Build Phases -> Run script write script to:
Build 2 separate frameworks:
1. Framework with architectures for mobile devices (armv7, arm64, etc.)
2. Framework with architectures for simulator (i386 and x86_64).
For example:
xcodebuild -workspace MyApp.xcworkspace -scheme MyFrameworkScheme -arch i386 -arch x86_64 ONLY_ACTIVE_ARCH=NO -sdk iphonesimulator -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator BUILD_DIR=${BUILD_DIR}
xcodebuild -workspace MyApp.xcworkspace -scheme MyFrameworkScheme -arch armv7 -arch armv7s -arch arm64 ONLY_ACTIVE_ARCH=NO -sdk iphoneos -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos BUILD_DIR=${BUILD_DIR}
Then, use lipo to merge the libraries inside both frameworks to a fat library, and replace one of them inside the framework => you have a fat framework.
I am building an iOS static library and I want to provide support for bitcode. In order to achieve that I go to Build settings, search for "custom compiler flags" and add -fembed-bitcode. This builds the library with bitcode and everything works fine under XCode 7.
However by following the approach above I loose backwards compatibility with XCode 6. Having that said I have to ship 2 different library versions to my users, one with bitcode flag and one without since not everyone has upgraded to XCode 7.
Is there a way to have bitcode enabled library and have backwards compatibility without having to ship 2 different versions?
UPDATE:
Hello #Vinicius Jarina thank you for your message. I understand that you can create a fat library which I guess is a common practise. What I was doing so far was to build for both architecture:
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdK iphoneos
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator
and then call lipo to package in a fat library like:
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}"
However, how can i do it now? I tried something like this based on this link, but with no luck:
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator
xcodebuild -configuration "Release" OTHER_CFLAGS='-fembed-bitcode' -target "${FMK_NAME}" -sdk iphonesimulator
xcodebuild -configuration "Release" OTHER_CFLAGS='-fembed-bitcode' -target "${FMK_NAME}" -sdk iphoneos
and then create a fat lib like this:
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}"
how can i build in my scipt to include both and then package them in a fat-library?
UPDATE 2:
I finally managed to make this work and I list here my solution for others that may face the same issue:
xcodebuild -configuration "Release" ENABLE_BITCODE=NO -target "${FMK_NAME}" -sdK iphoneos
xcodebuild -configuration "Release" ENABLE_BITCODE=NO -target "${FMK_NAME}" -sdk iphonesimulator
xcodebuild -configuration "Release" ENABLE_BITCODE=YES -target "${FMK_NAME}" -sdk iphonesimulator
xcodebuild -configuration "Release" ENABLE_BITCODE=YES -target "${FMK_NAME}" -sdk iphoneos
and then create a fat lib like this:
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}"
You can try to create a fat-library using different libraries.
lipo -create -output libAndreasv.a libAndreasvBitcode.a libAndreasvARMv7.a libAndreasvARM64.a
This used to work for fat libraries (x86,x64,ARMv7,ARM64) should work for bitcode as well.
Perhaps I am missing something but I do not believe you can have duplicate architectures in a fat library, regardless of whether bitcode is enabled/disabled. For instance the following command causes an error for me:
lipo -create libcurl_iOS_bitcode.a libcurl_iOS_nobitcode.a -output libcurl_iOS_both.a
fatal error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo: libcurl_iOS_bitcode.a and libcurl_iOS_nobitcode.a have the same architectures (armv7) and can't be in the same fat output file