XCFramework 采坑记

Building for iOS Simulator, but the embedded framework ‘xxx.framework’ was built for iOS + iOS Simulator.

升级 Xcode 后就悲剧了,以上报错苹果在 Xcode 11 中已经给出 warring,在 Xcode 12.3 版本后会直接 error。

来自苹果工程师的回复:

This framework isn’t built with a supported configuration – iOS and iOS Simulator code has never been supported in the same binary. The linker in Xcode 11 began identifying these incorrect configurations and issuing warnings, and Xcode 12 goes further in identifying these issues.

The only correct way to resolve this is to rebuild the framework as an XCFramework. If this is your framework, or owned by another group in your company, follow the information in the video and the Xcode Help article.

If this framework is from a vendor, then you need to work with the vendor to get an updated version of the framework built with supported configuration.

In the discussion of this thread, there is a build script that attempts to resolve this error. Scripts like that – anything that tries to manipulate the output with commands like lipo – still produces an unsupported configuration in the binary. XCFrameworks are the way to go.

官方推荐方式(需要 new build system 支持):

将库打包成 xcframework 格式。

有关于 xcframework 内容可以参考苹果的 WWDC 视频 Binary Frameworks in Swift

Carthage 目前通过 homebrew 安装方式还不支持打包成 xcframework 格式,所以需要我们手动调用 xcodebuild 命令。为了使用方便,我们可以在工程中添加 Aggregated target,然后创建 script 方便我们生成 xcframework。完整的 script 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
WORKSPACE_NAME="${PROJECT}.xcworkspace"
SCHEME_NAME=${PROJECT}
FRAMEWORK_NAME=${PROJECT}
SIMULATOR_ARCHIVE_PATH="${BUILD_DIR}/${CONFIGURATION}/${FRAMEWORK_NAME}-iphonesimulator.xcarchive"
DEVICE_ARCHIVE_PATH="${BUILD_DIR}/${CONFIGURATION}/${FRAMEWORK_NAME}-iphoneos.xcarchive"
OUTPUT_DIC="./xcframework/"

script="./xcodebuild.sh"

# Simulator xcarchieve
/bin/sh ${script} archive \
-workspace ${WORKSPACE_NAME} \
-scheme ${SCHEME_NAME} \
-archivePath ${SIMULATOR_ARCHIVE_PATH} \
-sdk iphonesimulator \
SKIP_INSTALL=NO

# Device xcarchieve
xcodebuild archive \
-workspace ${WORKSPACE_NAME} \
-scheme ${SCHEME_NAME} \
-archivePath ${DEVICE_ARCHIVE_PATH} \
-sdk iphoneos \
SKIP_INSTALL=NO

# Clean up old output directory
rm -rf "${OUTPUT_DIC}"

# Create xcframwork combine of all frameworks
xcodebuild -create-xcframework \
-framework ${SIMULATOR_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework \
-framework ${DEVICE_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework \
-output ${OUTPUT_DIC}/${FRAMEWORK_NAME}.xcframework

由于模拟器的编译在 Xcode 12.x 中有问题,所以新建了一个 xcodebuild.sh 文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env bash

# xcodebuild.sh
# Usage example: ./xcodebuild.sh archive

set -euo pipefail

xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
trap 'rm -f "$xcconfig"' INT TERM HUP EXIT

# For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise
# the build will fail on lipo due to duplicate architectures.
echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig
echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig

export XCODE_XCCONFIG_FILE="$xcconfig"
xcodebuild "$@"

两个临时修复方案:

  • 不使用 Xcode 12.3,换成 Xcode 12.2。
  • 将工程的 Validate Workspace 设置成为 YES。

参考文档: