How to build a Fat Framework that includes Mac Catalyst? - ios

How does one build a fat framework that includes the architectures necessary to build to Mac Catalyst apps?

Apple has introduced a (undocumented?) new target: x86_64-apple-ios13.0-macabi
How to build for this target depends on your frameworks build environment.
1) XCFramework
In case your framework is an Xcode project:
Select the target in Xcode
Select the "General" tab
Under "Deployment Info", tick the "Mac" checkbox:
build
2) External Build
In case you are building your framework outside Xcode, e.g. a C lib, instead of building for x86_64 & iphonesimulator, build for the new target x86_64-apple-ios13.0-macabi & macosx.
Example for C Lib using make:
MIN_IOS_VERSION="10.0"
LIB_NAME= "theNameOfYourLib"
# The build function
build()
{
ARCH=$1
TARGET=$2
HOST=$3
SDK=$4
SDK_PATH=`xcrun -sdk ${SDK} --show-sdk-path`
export PREFIX=build/${ARCH}
export CFLAGS="-arch ${ARCH} -isysroot ${SDK_PATH} -miphoneos-version-min=${MIN_IOS_VERSION} -std=c99 -target ${TARGET}"
export LDFLAGS="-arch ${ARCH}"
export CC="$(xcrun --sdk ${SDK} -f clang) -arch ${ARCH} -isysroot ${SDK_PATH}"
PKG_CONFIG_ALLOW_CROSS=1 PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig ./configure --host=${HOST} --prefix=$PREFIX
make
make install
}
# Build for all required architectures
build "armv7" "armv7-apple-ios" "arm-apple-darwin" "iphoneos" # MIN_IOS_VERSION must be one of arm7 supported ones to. Else remove this line.
build "arm64" "aarch64-apple-ios" "arm-apple-darwin" "iphoneos"
# build "x86_64" "x86_64-apple-ios" "x86_64-apple-darwin" "iphonesimulator" #obsolete due to x86_64-apple-ios13.0-macabi
build "x86_64" "x86_64-apple-ios13.0-macabi" "x86_64-apple-darwin" "macosx"
build "i386" "i386-apple-ios" "i386-apple-darwin" "iphonesimulator" # same as arm7: MIN_IOS_VERSION must be one of arm7 supported ones.
# Now find all the artefacts created above (e.g. build/arm64/lib/${LIB_NAME}.a, build/x86_64/lib/${LIB_NAME}.a ...) and merge them together to a fat lib using lipo
OUTPUT_DIR="fatLib"
lipo -create -output $OUTPUT_DIR/lib/${LIB_NAME}.a build/x86_64/lib/${LIB_NAME}.a build/arm64/lib/${LIB_NAME}.a build/armv7/lib/${LIB_NAME}.a build/i386/lib/${LIB_NAME}.a
# You may also need the header files
cp -R build/armv7/include/* $OUTPUT_DIR/include/
Note: You must/can not add slices for x86_64-apple-ios and x86_64-apple-ios13.0-macabito the fat lib. Both are x86_64. Use only the one for x86_64-apple-ios13.0-macabi.

Related

Could not find module 'MyCustomFramework' for target 'x86_64-apple-ios-simulator'; found: arm64, armv7-apple-ios, arm64-apple-ios, arm, armv7

I have a custom framework that I am archiving for use in another project. After updating to Xcode11 I get the following error in my project utilizing the framework.
Could not find module 'MyCustomFramework' for target 'x86_64-apple-ios-simulator'; found: arm64, armv7-apple-ios, arm64-apple-ios, arm, armv7
My framework works fine on Xcode10 but does not on 11. My target is set to NO for build active architectures only. I have the following for Valid Architectures; arm64e, armv7s, armv7, arm64.
My build script
exec > ${PROJECT_DIR}/${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}"
echo "Building for iPhoneSimulator"
xcodebuild -workspace "${WORKSPACE_PATH}" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 11 Pro Max' 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}"
# Step 4. Create universal binaries for embedded frameworks
#for SUB_FRAMEWORK in $( ls "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks" ); do
#BINARY_NAME="${SUB_FRAMEWORK%.*}"
#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
ARCHIVE_PATH="${PROJECT_DIR}/Products"
rm -rf "${ARCHIVE_PATH}"
mkdir "${ARCHIVE_PATH}"
echo "Copying to project dir"
yes | cp -Rf "${UNIVERSAL_OUTPUTFOLDER}/${FULL_PRODUCT_NAME}" "${ARCHIVE_PATH}"
open "${ARCHIVE_PATH}"
fi
This line is incorrect
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule"
Should be
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/"
You'll need to check in the target folder
"${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/"
If there are files like
x86_64-apple-ios-simulator.swiftdoc
x86_64-apple-ios-simulator.swiftmodule
x86_64.swiftdoc
x86_64.swiftmodule
Before Xcode 11, Apple seems neglect those files so your script works well.
From Xcode 11 those files are seem to be checked rigorously.
Swift 5.0-5.1, Xcode 11 Open Xcode, <your project>, Build Settings, Build Active Architecture Only and change to <NO> for Debug and Release. Architectures set/leave in Standard Architecture -$(ARCHS_STANDART), important is next step:Valid Architecture: armv7, armv7s, amr64, amr64e, ADD here x86_64 and if you need add i386 for Debug and Release. (String: armv7, armv7s, amr64, amr64e, x86_64)
Choose any simulator in your simulator list and BUILT IT. DONE.
I hope it is works for you.
Description of Architecture:
armv64: iPhoneX, iPhone 5s-8, iPad Air — iPad Pro
armv7 : iPhone3Gs-5c, iPad WIFI(4th gen)
armv6 : iPhone — iPhone3G
-the above if for real devices
i386 : 32-bit simulator
x86_64 : 64-bit simulator

Undefined symbols for architecture x86_64 using Swagger API:

I am having this error in my Xcode project....So basically what I am doing is creating a static library that uses a Swagger Api that I have installed using CocoaPods. I am able to import the header files and use the header files in the library, but when I put the static library in another Xcode project I receive this error where It says _OBJC_CLASS_$_SWG....", referenced from: objc-class-ref in Sendable.o.... which is basically the class where I am using the Swagger header files.....
Things I have attempted:
1: Delete Derived Data folder, cleaned and rebuilt
2: made sure POD, library, and project using library have the same architectures as well as
3: made sure architecture build active is set to NO for all
4: made sure $(inherited) is in both library and project using library
5: installed pod file again in library
Things I noticed:
1: the library itself does not have POD in the header search path
2: I get the following warnings but ignored them
3: using Pod update gives me error saying GitHub is offline make sure you have internet connection, only removing and installing seem to work successfully.
My guess is that you are building the library for a single architecture - you are building it either for a Generic iOS Device, or some speciffic iOS device. Either way, you will not be able to use the library on a simulator (x86_64 architecture). Vice versa, if you build the library for a simulator, you will not be able to use it on a device.
If you want to use the same build on both devices and simulator, you need to create an universal library using a tool called lipo, either manually or by Build Phase script. Check out this article. Short summary:
Add an Aggregate target to your library project
Add a Run Script Build Phase
Paste this script into it:
# define output folder environment variable
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# 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}"
xcodebuild -target ${PROJECT_NAME} -configuration ${CONFIGURATION} -sdk iphonesimulator -arch x86_64 -arch i386 -arch armv7 -arch armv7s -arch arm64 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step 2. Create universal binary file using lipo
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a"
echo "Universal library can be found here:"
echo ${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a
# Last touch. copy the header files. Just for convenience
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_OUTPUTFOLDER}/"
Build your Aggregate target and an universal library will be built

How to build cocoa touch framework for all architectures dependent on other frameworks added using cocoapods?

I want to create a private cocoapod of the output framework built for both simulators and devices.
I have created a Cocoa touch framework in which I have integrated CocoaLumberjack using Cocoapods.
I want to build the framework for all architectures possible (simulator as well as device).
By default the build setting,
'Build Active Architectures Only' is set to (Debug - Yes, Release - No).
As soon as I set this setting for debug to No, the build fails with the following linker error:
Undefined symbols for architecture i386:
"_OBJC_CLASS_$_DDASLLogger", referenced from:
objc-class-ref in MyManager.o
"_OBJC_CLASS_$_DDLog", referenced from:
objc-class-ref in MyManager.o
"_OBJC_CLASS_$_DDTTYLogger", referenced from:
objc-class-ref in MyManager.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I get it that CocoaLumberjack is available for only active architectures in debug version.
So I switch back the Build active architectures for debug to Yes and build the framework successfully.
To build the framework for all the architectures I am using a run script added in Build phases that also claims to merge the ios-device and ios-simulator build into one. Here is the script:
set -e
set +u
# Avoid recursively calling this script.
if [[ $SF_MASTER_SCRIPT_RUNNING ]]
then
exit 0
fi
set -u
export SF_MASTER_SCRIPT_RUNNING=1
# Constants
SF_TARGET_NAME=${PROJECT_NAME}
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# Take build target
if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]
then
SF_SDK_PLATFORM=${BASH_REMATCH[1]}
else
echo "Could not find platform name from SDK_NAME: $SDK_NAME"
exit 1
fi
if [[ "$SF_SDK_PLATFORM" = "iphoneos" ]]
then
echo "Please choose iPhone simulator as the build target."
exit 1
fi
IPHONE_DEVICE_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos
# Build the other (non-simulator) platform
xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/arm64" SYMROOT="${SYMROOT}" ARCHS='arm64' VALID_ARCHS='arm64' $ACTION
xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/armv7" SYMROOT="${SYMROOT}" ARCHS='armv7 armv7s' VALID_ARCHS='armv7 armv7s' $ACTION
# Copy the framework structure to the universal folder (clean it first)
rm -rf "${UNIVERSAL_OUTPUTFOLDER}"
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework"
# Smash them together to combine all architectures
lipo -create "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/arm64/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/armv7/${PROJECT_NAME}.framework/${PROJECT_NAME}" -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}"
I check the 'Run script only when installing' checkbox present below the run script. Now I build the framework for Generic iOS device.
Now i click on the Products group present in the Project hierarchy and select the MyFramework.framework file, right click and select show in finder. It opens up the ~/Library/Developer/Xcode/DerivedData/MyFramework-dlpsipmxkqmemwgqrfeovlzgyhca/Build/Products/Debug-iphoneos/MyFramework.framework in finder.
Now, I create a new tag 'MyFramework-v0.0.1' containing the commit where I added MyFramework.framework file.
I go to ~/.cocoapods/repos/MyRepoName/ and create a podspec file MyFramework.podspec as follows:
Pod::Spec.new do |s|
s.name = "MyFramework"
s.version = "0.0.1"
s.summary = "A pod for MyFramework"
s.description = "A pod designed for MyFramework"
s.homepage = "My_Private_Repo_Path"
s.license = { :type => "MIT", :file => "FILE_LICENSE" }
s.authors = { "My_Username" => "my_email_address"
}
s.platform = :ios, "8.0"
s.ios.deployment_target = "8.0"
s.source = { :git => "My_Private_Repo_Path", :tag => 'MyFramework-v'+String(s.version) }
s.requires_arc = true
s.vendored_frameworks = "MyFramework.framework"
s.dependency "CocoaLumberjack"
end
Now when I run the following command in the terminal:
pod repo push MyRepoName MyFramework.podspec
I get the following Error:
Validating spec
-> MyFramework (0.0.1)
- ERROR | [iOS] xcodebuild: Returned an unsuccessful exit code. You can use ` --verbose` for more information.
- NOTE | [iOS] xcodebuild: ld: warning: ignoring file MyFramework/MyFramework.framework/MyFramework, missing required architecture i386 in file MyFramework/MyFramework.framework/MyFramework (2 slices)
- NOTE | [iOS] xcodebuild: ld: warning: ignoring file MyFramework/MyFramework.framework/MyFramework, missing required architecture x86_64 in file MyFramework/MyFramework.framework/MyFramework (2 slices)
- NOTE | [iOS] xcodebuild: fatal error: lipo: -remove's specified would result in an empty fat file
[!] The `MyFramework.podspec` specification does not validate.
How to build cocoa touch framework for all devices and simulators, that is dependent on another framework (CocoaLumberjack) added using cocoapods? I need to create a private pod of the output framework.
So basically I found out that I just need to follow these simple steps.
1. Create a cocoa touch framework.
2. Set bitcode enabled to No.
3. Select your target and choose edit schemes. Select Run and choose Release from Info tab.
4. No other setting required.
5. Now build the framework for any simulator as simulator runs on x86 architecture.
6. Click on Products group in Project Navigator and find the .framework file.
7. Right click on it and click on Show in finder. Copy and paste it in any folder, I personally prefer the name 'simulator'.
8. Now build the framework for Generic iOS Device and follow the steps 6 through
9. Just rename the folder to 'device' instead of 'simulator'.
10. 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.
Now, creating a pod for this framework doesn't make any difference. It works like a charm. Please also note that there are run scripts available too to achieve the same functionality, but I spent a lot of time in finding the correct script. So I would suggest you use this method.
XCode 11
First note that you can not use a fat framework with simulator support (x84_64 arch) for publishing to AppStore so you need to make two fat frameworks: one for Release with ARM archs (devices only) and one for Debug - ARM and x86_64 archs.
You can put next scripts to your projects's folder to make fat frameworks from a command line:
Makefile
BUILD_DIR = build
BUILD = #sh build.sh ${BUILD_DIR}
default:
#echo "Build framework makefile"
#echo "usage: make (release | debug | all | rebuild | clean)"
release:
${BUILD} Release YourTargetScheme # <- your target scheme
debug:
${BUILD} Debug YourTargetScheme
clean:
rm -r ${BUILD_DIR}
all: release debug
rebuild: clean all
build.sh
# Debug
set -x
# Params
BUILD_DIR=$1
CONFIGURATION=$2
SCHEME=$3
WORKSPACE=YourWorkspace.xcworkspace # <- your workspace file
DERIVED_DATA_PATH=$BUILD_DIR/DerivedData
# Destinations
IPNONEOS="generic/platform=iOS"
IPNONESIMULATOR="platform=iOS Simulator,name=iPhone 8"
# Build
if [ $CONFIGURATION = "Release" ]; then
xcodebuild \
-quiet \
-showBuildTimingSummary \
-workspace $WORKSPACE \
-configuration $CONFIGURATION \
-scheme $SCHEME \
-derivedDataPath $DERIVED_DATA_PATH \
-destination "$IPNONEOS"
else
xcodebuild \
-quiet \
-showBuildTimingSummary \
-workspace $WORKSPACE \
-configuration $CONFIGURATION \
-scheme $SCHEME \
-derivedDataPath $DERIVED_DATA_PATH \
-destination "$IPNONEOS" \
-destination "$IPNONESIMULATOR"
fi
# Move
FRAMEWORK=$SCHEME.framework
FRAMEWORK_PATH=$BUILD_DIR/$CONFIGURATION/$FRAMEWORK
mkdir $BUILD_DIR/$CONFIGURATION
rm -r $FRAMEWORK_PATH
if [ $CONFIGURATION = "Release" ]; then
mv $DERIVED_DATA_PATH/Build/Products/Release-iphoneos/$FRAMEWORK $FRAMEWORK_PATH
else
mv $DERIVED_DATA_PATH/Build/Products/Debug-iphoneos/$FRAMEWORK $FRAMEWORK_PATH
BINARY_FILE=$FRAMEWORK_PATH/$SCHEME
ARMV7=$FRAMEWORK_PATH/armv7
ARM64=$FRAMEWORK_PATH/arm64
x86_64=$FRAMEWORK_PATH/x86_64
lipo $BINARY_FILE -extract armv7 -o $ARMV7
lipo $BINARY_FILE -extract arm64 -o $ARM64
cp $DERIVED_DATA_PATH/Build/Products/Debug-iphonesimulator/$FRAMEWORK/$SCHEME $x86_64
lipo -create $ARMV7 $ARM64 $x86_64 -o $BINARY_FILE
# Clean
rm -rf $ARMV7 $ARM64 $x86_64
fi
Run one of these commands inside your project folder to build needed frameworks:
make all # Debug and Release frameworks
make release # Release only for devices and AppStore (armv7 and arm64 archs)
make debug # Debug with simulator support (armv7, arm64 and x86_64 archs)
Then you can find your fat frameworks in build directory inside your project's folder.

Bitcode and dylib

I am trying to compile a C library to use it in my iOS project, and I want to embed bitcode.
I can successfully build static libraries targeting each arch. And those static library do contain bitcode (checked using otool), but the dynamic library doesn't contain bitcode. Why? Is bitcode not supported in dylib?
The library I am trying to build is xz. Here is the script
build_iOS()
{
ARCH=$1
if [ $ARCH == "i386" ] || [ $ARCH == "x86_64" ];
then
SDKROOT="$(xcodebuild -version -sdk iphonesimulator | grep -E '^Path' | sed 's/Path: //')"
else
SDKROOT="$(xcodebuild -version -sdk iphoneos | grep -E '^Path' | sed 's/Path: //')"
fi
export CC="$(xcrun -sdk iphoneos -find clang)"
export CFLAGS="-fembed-bitcode -isysroot $SDKROOT -arch ${ARCH} -miphoneos-version-min=9.0"
export LDFLAGS="-arch ${ARCH} -isysroot $SDKROOT"
if [ $ARCH == "i386" ] || [ $ARCH == "x86_64" ];
then
./configure --prefix=$XZPATH/build/iOS/$ARCH --host=i686-apple-darwin11 --disable-static --enable-shared
else
./configure --prefix=$XZPATH/build/iOS/$ARCH --host=arm-apple-darwin --disable-static --enable-shared
fi
make && make install && make clean
}
build_iOS i386
build_iOS x86_64
build_iOS armv7
build_iOS armv7s
build_iOS arm64
Thanks!
It looks like I cannot add bitcode to dylibs. I tried building several dylibs, then use otool -l path_to_dylib | grep bitcode to test if they contain any bitcode, all got nothing.
More evidence:
in Xcode(7.3.1), macOS (previously called OS X) targets don't have enable bitcode option in build settings
in the bitcode section of App Thinning, Apple didn't mentioned bitcode on macOS. Plus, App Thinning is only available on iOS, watchOS and tvOS.
I currently don't know why macOS apps don't have enable bitcode option in build setting. Maybe it's because Mac App store is not the only way for distributing mac apps? And people might copy one mac app from one mac app to another using USB sticks?
I was not able to verify bitcode in my bitcode enabled dynamic library via cmd line tools (otool, file or clang). Also comparing the diff between bitcode and non-bitcode build showed no difference, except the filesize.
Interestingly when using the dynamic library withouth bitcode enabled in an actual app and archiving xcode will fail to archive when I use the non-bitcode build:
ld: bitcode bundle could not be generated because '...' was built without full bitcode. All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build for architecture armv7
When building the dylib with bitcode enabled the filesize increases a lot and also xcode does not fail on archiving a sample project with bitcode enabled. So I am pretty sure that bitcode must be included in the dynamic lib, although we haven't found a way to verify that via cmd line tools yet...

how to build ios framework to support i386 and armv7

I need to build dynamic library as framework. I have building settings as follows:
ARCHS: armv7 armv7s arm64 i386 x86_64
ONLY_ACTIVE_ARCH: NO
VALID_ARCHS: arm64 armv7 armv7s x86_64 i386
I built target for ios device, and used lipo -info to check the architectures, the result is :
Architectures in the fat file: dyl are: armv7s armv7 arm64
So, does xcode can not build for both i386 and arm?
You need to select iOS simulator and build a framework for i386 arch. Then you can use lipo -create command to merge the two framework into one.
There is a way of doing it without exiting the Xcode. First you need to identify the latest iOS versions (64 bits) do not support i386 architecture. So if you still need i386 compatible framework, you have to build your project by selecting an old iOS version. I tried with iOS 10.
Next thing that you have to do is, add all the architectures that you need under Valid Architectures filed in the Build Settings.
Then, add a Run Script with your lipo commands to create the FAT file. (Following script I've taken from the Internet sometime back & credit goes to those who wrote that)
#!/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 "${PROJECT_DIR}"
Finally what you have to do is, build the framework by selecting Generic iOS Device option.
Then to verify, run the lipo -info command

Resources