Cocoa Touch Frameworks - Bitcode support - ios

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

Related

iOS app distribution failed with error ITMS-90635 nested bundle contains arm64(machine code) whereas main bundle contains arm64(bitcode)

I have made my own fat framework that I distribute over Cocoapods.
But when I try to upload an app with bitcode enabled using my framework, I am rejected with error ITMS-90635, see:
I want my framework to be compatible with bitcode, so I set ENABLE_BITCODE=YES, BITCODE_GENERATION_MODE=bitcode (also tried with OTHER_CFLAGS="-fembed-bitcode" in addition) when building the framework.
Here is how I build my framework:
[...]
echo "Clean ${TARGET_NAME} for simulator"
xcodebuild -workspace ${WORKSPACE_NAME}.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' -scheme ${SCHEME} CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ENABLE_BITCODE=YES BITCODE_GENERATION_MODE=bitcode clean
echo "Clean ${TARGET_NAME} for generic device"
xcodebuild -workspace ${WORKSPACE_NAME}.xcworkspace -configuration ${CONFIGURATION} -destination generic/platform=iOS -scheme ${SCHEME} CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ENABLE_BITCODE=YES BITCODE_GENERATION_MODE=bitcode clean
echo "Build ${WORKSPACE_NAME} for simulator"
xcrun xcodebuild -workspace ${WORKSPACE_NAME}.xcworkspace -scheme ${SCHEME} -configuration ${CONFIGURATION} -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' -sdk iphonesimulator CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ENABLE_BITCODE=YES BITCODE_GENERATION_MODE=bitcode
echo "Build ${WORKSPACE_NAME} for generic device"
xcrun xcodebuild -workspace ${WORKSPACE_NAME}.xcworkspace -scheme ${SCHEME} -configuration ${CONFIGURATION} -destination generic/platform=iOS -sdk iphoneos CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ENABLE_BITCODE=YES BITCODE_GENERATION_MODE=bitcode
[...]
lipo -create "${DEVICE_BIN}/${TARGET_NAME}" "${SIMULATOR_BIN}/${TARGET_NAME}" -output "${UNIVERSAL_PATH}/${TARGET_NAME}.framework/${TARGET_NAME}"
What I really do not understand is that my framework seems to have bitcode enabled, see:
Am I doing something wrong when building my framework?
Ok, I finally found what was wrong :)
Once lipo done, I used to copy/paste
Build/Products/Release-iphoneos/MyFramework.framework/Modules/MyFramework.swiftmodule/
into
Build/Products/Release-iphonesimulator/MyFramework.framework/Modules/MyFramework.swiftmodule/
and then I was distributing this final framework:
Build/Products/Release-iphonesimulator/MyFramework.framework.
Do the opposite! Once lipo done, copy/paste MyFramework.swiftmodule/'s Release-iphonesimulator/ into the Release-iphoneos/ one and distribute the .framework in Release-iphoneos/.

Creating iOS Framework for device and Universal configuration with bitcode enable and bitcode disable from Command Line terminal

#!/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 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 "${PROJECT_DIR}"
I have above shell script to create framework for device and universal from Xcode build options.
Is is possible to create/Generate iOS Framework from Command terminal with above shell script with below configuration???
1) Create framework for device bitcode disable
2) Create framework for device bitcode enable
3) Create framework for Universal bitcode disable
4) Create framework for Universal bitcode enable
Please let me know the steps to create/generate iOS Framework from
command terminal. Must appreciate for the best explanation
Just add the argument to your xcodebuild
ENABLE_BITCODE=NO for disabling bitcode
ENABLE_BITCODE=YES for enabling bitcode
For example
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build ENABLE_BITCODE=NO
In the shell script you posted, replace the code under # Step 1. Build Device and Simulator versions with the following appropriately.
1) Create framework for device bitcode disable:
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO \
-configuration ${CONFIGURATION} -sdk iphoneos \
BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build ENABLE_BITCODE=NO
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} \
-sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" \
BUILD_ROOT="${BUILD_ROOT}" clean build ENABLE_BITCODE=YES
2) Create framework for device bitcode enable
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO \
-configuration ${CONFIGURATION} -sdk iphoneos \
BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build ENABLE_BITCODE=YES
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} \
-sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" \
BUILD_ROOT="${BUILD_ROOT}" clean build ENABLE_BITCODE=YES
3) Create framework for Universal bitcode disable
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO \
-configuration ${CONFIGURATION} -sdk iphoneos \
BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build ENABLE_BITCODE=NO
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} \
-sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" \
BUILD_ROOT="${BUILD_ROOT}" clean build ENABLE_BITCODE=NO
4) Create framework for Universal bitcode enable
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO \
-configuration ${CONFIGURATION} -sdk iphoneos \
BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build ENABLE_BITCODE=YES
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} \
-sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" \
BUILD_ROOT="${BUILD_ROOT}" clean build ENABLE_BITCODE=YES
EDIT:
To build the framework, you can do either of the below:
1. Replacing the code for each config, go to terminal and run the shell script by navigating to the folder containing the shell script and running ./<nameOfTheShellScriptFile>.sh. But make sure you have the build settings available there. That will create the framework with the appropriate configuration in the directory ${BUILD_DIR}/${CONFIGURATION}-universal.
2. Add a run script in the Xcode. Open your project in Xcode, click on the schemes dropdown (beside Stop button) and choose your framework scheme. Open the dropdown again and click on "Edit Scheme...". You see 6 actions to the left. You choose which action makes more sense to add the script to (do you want to create this universal framework on every Run action or only when you archive?). Drop down that action and click on Post-actions. Click on + button to add a new action and click on "New Run Script Action". Set the "Provide build settings from" to your framework target to get the appropriate build settings. Paste the script in the text area and click close. Now after every time you perform the action you put the script under, the script runs and builds the framework again for both device and simulator architectures and puts the universal framework in your project directory. It takes some time for this to complete especially if your project is big so wait patiently until the finder opens up to reveal your project directory containing the framework. Now you can either change the code in the script every time you want a different config (bitcode enabled or disabled), or add different schemes for different config and follow this whole step for each of the schemes with appropriate script.

Unable to build ios app for simulator using xcodebuild

I am currently want to create a debug simulator build using xcodebuild command rather than xcode IDE.
however, I always see a "debug-iphoneos" folder is generated, which is for the real device. I expected the simulator output folder "Debug-iphonesimulator"
Can anyone take a look at my script and suggest why the script is not working for simulator?
xcodebuild -scheme "TestApp" -configuration "Debug" -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' -workspace TestApp.xcworkspace clean archive -archivePath build/TestApp PROVISIONING_PROFILE="$provisioningUUID" CODE_SIGN_IDENTITY="$codeSignIdentity"
now I can run successfully as below:
xcodebuild -arch i386 -sdk iphonesimulator9.3 -workspace "$app.xcworkspace" -scheme "$scheme" -configuration "Debug"
You should run the following instead:
xcodebuild -arch i386 -sdk iphonesimulator9.1 -workspace [name].xcworkspace -scheme [targetName or ProjectName] ONLY_ACTIVE_ARCH=NO VALID_ARCHS="i386 x86_64"
where iphonesimulator9.1 depends on which iPhone Simulator you installed.

Export Universal framework for distribution

i have created a framework
Now if reveal it in finder,
It have 2 directory having framework
iphoneos
iphone simmulater
Now i create an app, and drag my framework from iphoneos directory, it run fine in iphone device but gives error in simmulater.
similarely if i drag framework from iphone simulater it work fine in simmulater but give error in device.
Please how to export both directory framework in a combine.
i have try to run script of lipo using aggreation target, but it fails
please guide proper steps.
You can add similar script to your build settings Build Phase tab as Run Script
# debug sim
xcrun xcodebuild -project myFramework.xcodeproj -target myFramework -configuration Debug -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO
# release sim
xcrun xcodebuild -project myFramework.xcodeproj -target myFramework -configuration Release -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO
# debug ios
xcrun xcodebuild -project myFramework.xcodeproj -target myFramework -configuration Debug -sdk iphoneos ONLY_ACTIVE_ARCH=NO
# release ios
xcrun xcodebuild -project myFramework.xcodeproj -target myFramework -configuration Release -sdk iphoneos ONLY_ACTIVE_ARCH=NO
mkdir -p build/Release-Universal/myFramework.framework
cp -r build/Debug-iphonesimulator/myFramework.framework/* build/Release-Universal/myFramework.framework/
rm build/Release-Universal/myFramework.framework/myFramework
# lipo
lipo -create build/Release-iphoneos/myFramework.framework/myFramework build/Release-iphonesimulator/myFramework.framework/myFramework -output build/Release-Universal/myFramework.framework/myFramework

Build an iOS library with Bitcode in order to have backwards compatibility with XCode 6. How?

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

Resources