Framework与XCFramework的制作

引言

由于Xcode11引入了XCFramework这个库概念,所以我们看到的二进制第三方库中都包含了两个文件,一个是.framework,而另外一个则是.xcframework,为什么多出了一个xcframework的文件,这个文件代表的意思是什么呢,以及如何制作这种新类型的二进制包?那首先我们必须先来了解几个概念。

Library

作用:

App的设计为了达到共享复制的目的,那么一般是把通用的函数或者功能模块制作成一个公共库。

定义:

目标文件的集合,即将相关的目标文件打包在一起,就成了一个 Library。

静态库(Static Library)

文件形式:

.a,.lib 的形式存在,iOS中还可以是.framework或.xcframework

使用原理:

App自身的代码被编译成目标文件后,通过静态链接器将App的目标文件与静态库合并,并生成可执行文件。这样,App自身代码生成的目标文件与静态库都被拷贝到可执行文件中,从而让静态库也成为了App可执行文件的一部分。

特点包括:

  • App启动时就全部加入内存,由于使用的是静态库,不需要从外部链接加载,所以加载速度很快,但是又因为增加了内容过多导致启动速度慢

  • 静态库文件会被静态链接器链接并复制到生成的可执行文件中,导致可执行文件增大,占用内存也会增多

  • 由于静态库是制作好的二进制库,如果发生变更,需要重新编译,给维护带来不便

动态库(Dynamic Library)

文件形式:

以.dylib,.so,.dll的形式存在,iOS中还可以是.framework或.xcframework

iOS App的动态库存放在.app bundle下的Frameworks 文件夹

使用原理:

以 OS X 为例,当 App 启动时,操作系统内核会将 App
代码和数据载入新进程(也就是操作系统为 App
创建的新进程)的地址空间。与此同时呢,操作系统内核也会把动态加载器(Dynamic
Loader)
载入进程,由动态加载器来完成加载App依赖的动态库。不过在启动阶段,动态加载器只会根据静态链接器中记录的
App 已链接的依赖库的名字,然后使用依赖库的 install
name来查找它们是否在文件系统中存在。如果不存在或不兼容,App
启动过程会中断。动态库被完全载入内存,是在代码里使用它的时候。所以相对静态库来说,使用动态库链接的
App 启动过程会更快。

特点包括:

  • 由于按需加载,可以加快App启动

  • 不会被加载复制到可执行文件中,所以可以动态按需加载

  • 维护更新方便,只要api不变,动态库变更,依赖动态库的App无需重新编译。因为重新编译主要是App的可执行文件部分,而动态库不属于其中,而是独立的,可动态加载

Framework

Framework通俗的理解为封装了共享资源的具有层次结构的文件夹。共享资源可以是
nib文件、国际化字符串文件、头文件、库文件等等。它同时也是个Bundle,里面的内容可以通过
Bundle相关 API来访问。Framework可以是static framework或dynamic
framework。在iOS App打包完成后,如果Framework包含了模拟器指令集(x86_64
或 i386),那么用 Xcode 发布 App 的时候,会报unsupported
architectures的错误,所以需要我们手动或脚本去移除。

XCFramework

XCFramework是由Xcode创建的一个可分发的二进制包,包含了framework或library
的一个或多个变体,因此可以在多个平台(iOS、macOS、tvOS、watchOS)上使用,包括模拟器。XCFramework可以是静态的也可以是动态的。XCFramework的好处就是用
Xcode
发布的时候,Xcode会自动选用正确的指令集Frameworks,省去了手动移除动态库中的模拟器指令集的工作。

制作 Frameworks

假设您的framework代码都已经写好了,打包的Aggregation
Target也创建好了。接下来,我将直接讲用脚本来制作frameworks。至于是制作static
framework还是dynamic framework可以在framework target的 Build
Settings中的Mach-O Type选择framework的类型,一般选用Dynamic
Library或者Static Library就行。

开发环境

Xcode11.0+

macOS Catalina 10.15.0+

.xcarchive 目录结构

制作universal
framework与xcframework,我们都会用到.xcarchive包,所以我们先来看下它的目录结构

制作Universal Framework脚本

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/bin/sh -e

REVEAL_FRAMEWORK_IN_FINDER=true

BUILD_DIR=\"\${PROJECT_DIR}/Build\"

FREAMEWORK_NAME=\"\${PROJECT_NAME}\"

FREAMEWORK_OUTPUT_DIR=\"\${PROJECT_DIR}/Distribution\"

ARCHIVE_PATH_IOS_DEVICE=\"\${BUILD_DIR}/ios_device.xcarchive\"

ARCHIVE_PATH_IOS_SIMULATOR=\"\${BUILD_DIR}/ios_simulator.xcarchive\"

\# ARCHIVE_PATH_MACOS=\"./build/macos.xcarchive\"

function archiveOnePlatform {

echo \"▸ Starts archiving the scheme: \${1} for destination: \${2};\\n▸
Archive path: \${3}\"

xcodebuild archive \\

-scheme \"\${1}\" \\

-destination \"\${2}\" \\

-archivePath \"\${3}\" \\

VALID_ARCHS=\"\${4}\" \\

SKIP_INSTALL=NO \\

BUILD_LIBRARY_FOR_DISTRIBUTION=YES \| xcpretty

\# sudo gem install -n /usr/local/bin xcpretty

\# xcpretty makes xcode compile information much more readable.

}

function archiveAllPlatforms {

\# https://www.mokacoding.com/blog/xcodebuild-destination-options/

\# Platform Destination

\# iOS generic/platform=iOS

\# iOS Simulator generic/platform=iOS Simulator

\# iPadOS generic/platform=iPadOS

\# iPadOS Simulator generic/platform=iPadOS Simulator

\# macOS generic/platform=macOS

\# tvOS generic/platform=tvOS

\# watchOS generic/platform=watchOS

\# watchOS Simulator generic/platform=watchOS Simulator

\# carPlayOS generic/platform=carPlayOS

\# carPlayOS Simulator generic/platform=carPlayOS Simulator

SCHEME=\${1}

archiveOnePlatform \$SCHEME \"generic/platform=iOS Simulator\"
\${ARCHIVE_PATH_IOS_SIMULATOR} \"x86_64\"

archiveOnePlatform \$SCHEME \"generic/platform=iOS\"
\${ARCHIVE_PATH_IOS_DEVICE} \"armv7 arm64\"

\# archiveOnePlatform \$SCHEME \"generic/platform=macOS\"
\${ARCHIVE_PATH_MACOS}

}

function makeUniversalFramework {

FRAMEWORK_RELATIVE_PATH=\"Products/Library/Frameworks\"

SIMULATOR_FRAMEWORK=\"\${ARCHIVE_PATH_IOS_SIMULATOR}/\${FRAMEWORK_RELATIVE_PATH}/\${FREAMEWORK_NAME}.framework\"

DEVICE_FRAMEWORK=\"\${ARCHIVE_PATH_IOS_DEVICE}/\${FRAMEWORK_RELATIVE_PATH}/\${FREAMEWORK_NAME}.framework\"

OUTPUT_FRAMEWORK=\"\${FREAMEWORK_OUTPUT_DIR}/\${FREAMEWORK_NAME}.framework\"

mkdir -p \"\${OUTPUT_FRAMEWORK}\"

\# Copy all the contents of iphoneos framework to output framework dir.

cp -rf \"\${DEVICE_FRAMEWORK}/.\" \"\${OUTPUT_FRAMEWORK}\"

lipo \"\${SIMULATOR_FRAMEWORK}/\${FREAMEWORK_NAME}\"
\"\${DEVICE_FRAMEWORK}/\${FREAMEWORK_NAME}\" \\

-create -output \"\${OUTPUT_FRAMEWORK}/\${FREAMEWORK_NAME}\"

\# For Swift framework, Swiftmodule needs to be copied in the universal
framework

if \[ -d\"\${SIMULATOR_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\" \];
then

cp -f
\"\${SIMULATOR_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\*\"
\"\${OUTPUT_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\" \| echo

fi

if \[ -d
\"\${DEVICE_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\" \];
then

cp -f \"\${DEVICE_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\*\"
\"\${OUTPUT_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\" \| echo

fi

}

echo \"#####################\"

echo \"▸ Cleaning Framework output dir: \${FREAMEWORK_OUTPUT_DIR}\"

rm -rf \"\$FREAMEWORK_OUTPUT_DIR\"

\#### Make Dynamic Framework

echo \"▸ Archive framework: \${FREAMEWORK_NAME}\"

archiveAllPlatforms \"\$FREAMEWORK_NAME\"

echo \"▸ Make universal framework: \${FREAMEWORK_NAME}.framework\"

makeUniversalFramework

\# Clean Build

rm -rf \"{\$BUILD_DIR}\"

if \[ \${REVEAL_FRAMEWORK_IN_FINDER} = true \]; then

open \"\${FREAMEWORK_OUTPUT_DIR}/\"

fi

编译单个平台的函数

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
\# 制作完 framework 后,是否在 Finder 中打开

REVEAL_FRAMEWORK_IN_FINDER=true

\# Framework 的名字

FREAMEWORK_NAME=\"\${PROJECT_NAME}\"

\# 制作好的 framework 会输出到这个文件夹下面

FREAMEWORK_OUTPUT_DIR=\"\${PROJECT_DIR}/Distribution\"

\# Device Archive 生成的 .xcarchive 存放路径。在工程的根目录下生成 Build
文件夹。

ARCHIVE_PATH_IOS_DEVICE=\"./Build/ios_device.xcarchive\"

\# Simulator Archive 生成的 .xcarchive 存放路径。

ARCHIVE_PATH_IOS_SIMULATOR=\"./Build/ios_simulator.xcarchive\"

\# 我们可以编译更多平台的 xcarchive

\# ARCHIVE_PATH_MACOS=\"./build/macos.xcarchive\"

\# 生成单个平台的 .xcarchive. 接收4个参数, scheme, destination,
archivePath,指令集.

\# xcpretty 可以删除,这里用来使 Xcode 输出的日志更加人性化。

function archiveOnePlatform {

echo \"▸ Starts archiving the scheme: \${1} for destination: \${2};\\n▸
Archive path: \${3}\"

xcodebuild archive \\

-scheme \"\${1}\" \\

-destination \"\${2}\" \\

-archivePath \"\${3}\" \\

VALID_ARCHS=\"\${4}\" \\

SKIP_INSTALL=NO \\

BUILD_LIBRARY_FOR_DISTRIBUTION=YES \| xcpretty

\# BUILD_LIBRARY_FOR_DISTRIBUTION=YES

\# sudo gem install -n /usr/local/bin xcpretty

\# xcpretty makes xcode compile information much more readable.

}

编译所有平台的函数

以下方法可以编译并生成iOS device,
simulator两个平台的.xcarchive,此方法接收一个参数:scheme, 即对应app
target的scheme。通常情况下,scheme和framework name是相同的。

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
34
35
36
37
38
function archiveAllPlatforms {

\# https://www.mokacoding.com/blog/xcodebuild-destination-options/

\# Platform Destination

\# iOS generic/platform=iOS

\# iOS Simulator generic/platform=iOS Simulator

\# iPadOS generic/platform=iPadOS

\# iPadOS Simulator generic/platform=iPadOS Simulator

\# macOS generic/platform=macOS

\# tvOS generic/platform=tvOS

\# watchOS generic/platform=watchOS

\# watchOS Simulator generic/platform=watchOS Simulator

\# carPlayOS generic/platform=carPlayOS

\# carPlayOS Simulator generic/platform=carPlayOS Simulator

SCHEME=\${1}

archiveOnePlatform \$SCHEME \"generic/platform=iOS Simulator\"
\${ARCHIVE_PATH_IOS_SIMULATOR} \"x86_64\"

archiveOnePlatform \$SCHEME \"generic/platform=iOS\"
\${ARCHIVE_PATH_IOS_DEVICE} \"armv7 arm64\"

\# archiveOnePlatform \$SCHEME \"generic/platform=macOS\"
\${ARCHIVE_PATH_MACOS}

}

这个方法执行完后,在本例中会得到以下Build文件夹内的内容:

生成Universal Framework的函数

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function makeUniversalFramework {

\# xcarchive 包中的 Frameworks 目录相对路径

FRAMEWORK_RELATIVE_PATH=\"Products/Library/Frameworks\"

\#
接下来的三个路径分别是模拟器平台的framework路径,真机平台的framework路径,以及输出的universal
framework路径

SIMULATOR_FRAMEWORK=\"\${ARCHIVE_PATH_IOS_SIMULATOR}/\${FRAMEWORK_RELATIVE_PATH}/\${FREAMEWORK_NAME}.framework\"

DEVICE_FRAMEWORK=\"\${ARCHIVE_PATH_IOS_DEVICE}/\${FRAMEWORK_RELATIVE_PATH}/\${FREAMEWORK_NAME}.framework\"

OUTPUT_FRAMEWORK=\"\${FREAMEWORK_OUTPUT_DIR}/\${FREAMEWORK_NAME}.framework\"

mkdir -p \"\${OUTPUT_FRAMEWORK}\"

\# Copy all the contents of iphoneos framework to output framework dir.

cp -rf \"\${DEVICE_FRAMEWORK}/.\" \"\${OUTPUT_FRAMEWORK}\"

lipo \"\${SIMULATOR_FRAMEWORK}/\${FREAMEWORK_NAME}\"
\"\${DEVICE_FRAMEWORK}/\${FREAMEWORK_NAME}\" \\

-create -output \"\${OUTPUT_FRAMEWORK}/\${FREAMEWORK_NAME}\"

\# For Swift framework, Swiftmodule needs to be copied in the universal
framework

if \[ -d\"\${SIMULATOR_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\" \];
then

cp -f
\"\${SIMULATOR_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\*\"
\"\${OUTPUT_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\" \| echo

fi

if \[ -d
\"\${DEVICE_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\" \];
then

cp -f \"\${DEVICE_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\*\"
\"\${OUTPUT_FRAMEWORK}/Modules/\${FRAMEWORK_NAME}.swiftmodule/\" \| echo

fi

}

开始制作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
echo \"#####################\"

echo \"▸ Cleaning Framework output dir: \${FREAMEWORK_OUTPUT_DIR}\"

rm -rf \"\$FREAMEWORK_OUTPUT_DIR\"

echo \"▸ Archive framework: \${FREAMEWORK_NAME}\"

archiveAllPlatforms \"\$FREAMEWORK_NAME\"

echo \"▸ Make universal framework: \${FREAMEWORK_NAME}.framework\"

makeUniversalFramework

\# Clean Build

rm -rf \"./Build\"

if \[ \${REVEAL_FRAMEWORK_IN_FINDER} = true \]; then

open \"\${FREAMEWORK_OUTPUT_DIR}/\"

fi

去除动态库中的模拟器指令集

App打包过程中,需要将App依赖的动态库中的模拟器指令集去除,这里是完整的脚本,
如何使用,请点Stripping unwanted architectures from dynamic
libraries。在app target 的 Build Phase下新建Run Script,并放到Embed
Frameworks下面,然后将脚本复制进去就行。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/bin/sh

APP_PATH=\"\${TARGET_BUILD_DIR}/\${WRAPPER_NAME}\"

echo \$APP_PATH

\# This script loops through the frameworks embedded in the application
and

\# removes unused architectures.

find \"\$APP_PATH\" -name \'\*.framework\' -type d \| while read -r
FRAMEWORK

do

FRAMEWORK_EXECUTABLE_NAME=\$(defaults read \"\$FRAMEWORK/Info.plist\"
CFBundleExecutable)

FRAMEWORK_EXECUTABLE_PATH=\"\$FRAMEWORK/\$FRAMEWORK_EXECUTABLE_NAME\"

echo \"Executable is \$FRAMEWORK_EXECUTABLE_PATH\"

EXTRACTED_ARCHS=()

for ARCH in \$ARCHS

do

echo \"Extracting \$ARCH from \$FRAMEWORK_EXECUTABLE_NAME\"

lipo -extract \"\$ARCH\" \"\$FRAMEWORK_EXECUTABLE_PATH\" -o
\"\$FRAMEWORK_EXECUTABLE_PATH-\$ARCH\"

EXTRACTED_ARCHS+=(\"\$FRAMEWORK_EXECUTABLE_PATH-\$ARCH\")

done

echo \"Merging extracted architectures: \${ARCHS}\"

lipo -o \"\$FRAMEWORK_EXECUTABLE_PATH-merged\" -create
\"\${EXTRACTED_ARCHS\[@\]}\"

rm \"\${EXTRACTED_ARCHS\[@\]}\"

echo \"Replacing original executable with thinned version\"

rm \"\$FRAMEWORK_EXECUTABLE_PATH\"

mv \"\$FRAMEWORK_EXECUTABLE_PATH-merged\"
\"\$FRAMEWORK_EXECUTABLE_PATH\"

done

制作XCFramework脚本

XCFramework脚本制作与Universal Framework流程方式一致,存在生成规则的不同

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/bin/sh -e

REVEAL_XCFRAMEWORK_IN_FINDER=true

FREAMEWORK_NAME=\"\${PROJECT_NAME}\"

FREAMEWORK_OUTPUT_DIR=\"\${PROJECT_DIR}/Distribution\"

ARCHIVE_PATH_IOS_DEVICE=\"./Build/ios_device.xcarchive\"

ARCHIVE_PATH_IOS_SIMULATOR=\"./Build/ios_simulator.xcarchive\"

\# ARCHIVE_PATH_MACOS=\"./build/macos.xcarchive\"

function archiveOnePlatform {

echo \"▸ Starts archiving the scheme: \${1} for destination: \${2};\\n▸
Archive path: \${3}\"

xcodebuild archive \\

-scheme \"\${1}\" \\

-destination \"\${2}\" \\

-archivePath \"\${3}\" \\

VALID_ARCHS=\"\${4}\" \\

SKIP_INSTALL=NO \\

BUILD_LIBRARY_FOR_DISTRIBUTION=YES \| xcpretty

\# sudo gem install -n /usr/local/bin xcpretty

\# xcpretty makes xcode compile information much more readable.

}

function archiveAllPlatforms {

\# https://www.mokacoding.com/blog/xcodebuild-destination-options/

\# Platform Destination

\# iOS generic/platform=iOS

\# iOS Simulator generic/platform=iOS Simulator

\# iPadOS generic/platform=iPadOS

\# iPadOS Simulator generic/platform=iPadOS Simulator

\# macOS generic/platform=macOS

\# tvOS generic/platform=tvOS

\# watchOS generic/platform=watchOS

\# watchOS Simulator generic/platform=watchOS Simulator

\# carPlayOS generic/platform=carPlayOS

\# carPlayOS Simulator generic/platform=carPlayOS Simulator

SCHEME=\${1}

archiveOnePlatform \$SCHEME \"generic/platform=iOS Simulator\"
\${ARCHIVE_PATH_IOS_SIMULATOR} \"x86_64\"

archiveOnePlatform \$SCHEME \"generic/platform=iOS\"
\${ARCHIVE_PATH_IOS_DEVICE} \"armv7 arm64\"

\# archiveOnePlatform \$SCHEME \"generic/platform=macOS\"
\${ARCHIVE_PATH_MACOS}

}

function makeXCFramework {

FRAMEWORK_RELATIVE_PATH=\"Products/Library/Frameworks\"

OUTPUT_DIR=\"\${FREAMEWORK_OUTPUT_DIR}/DynamicFramework\"

mkdir -p \"\${OUTPUT_DIR}\"

xcodebuild -create-xcframework \\

-framework
\"\${ARCHIVE_PATH_IOS_DEVICE}/\${FRAMEWORK_RELATIVE_PATH}/\${FREAMEWORK_NAME}.framework\"
\\

-framework
\"\${ARCHIVE_PATH_IOS_SIMULATOR}/\${FRAMEWORK_RELATIVE_PATH}/\${FREAMEWORK_NAME}.framework\"
\\

-output \"\${OUTPUT_DIR}/\${FREAMEWORK_NAME}.xcframework\"

}

echo \"#####################\"

echo \"▸ Cleaning XCFramework output dir: \${FREAMEWORK_OUTPUT_DIR}\"

rm -rf \$FREAMEWORK_OUTPUT_DIR

\#### Make XCFramework

echo \"▸ Archive framework: \${FREAMEWORK_NAME}\"

archiveAllPlatforms \$FREAMEWORK_NAME

echo \"▸ Make framework: \${FREAMEWORK_NAME}.xcframework\"

makeXCFramework

\# Clean Build

rm -rf \"./Build\"

if \[ \${REVEAL_XCFRAMEWORK_IN_FINDER} = true \]; then

open \"\${FREAMEWORK_OUTPUT_DIR}/\"

fi

生成Universal XCFramework的函数

制作XCFramewrok的时候,需要将Build Settings中的Build Libraries for
Distribution设置为YES。不过即使不设置,脚本也会将其设置为YES。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function makeXCFramework {

FRAMEWORK_RELATIVE_PATH=\"Products/Library/Frameworks\"

OUTPUT_DIR=\"\${FREAMEWORK_OUTPUT_DIR}/DynamicFramework\"

mkdir -p \"\${OUTPUT_DIR}\"

xcodebuild -create-xcframework \\

-framework
\"\${ARCHIVE_PATH_IOS_DEVICE}/\${FRAMEWORK_RELATIVE_PATH}/\${FREAMEWORK_NAME}.framework\"
\\

-framework
\"\${ARCHIVE_PATH_IOS_SIMULATOR}/\${FRAMEWORK_RELATIVE_PATH}/\${FREAMEWORK_NAME}.framework\"
\\

-output \"\${OUTPUT_DIR}/\${FREAMEWORK_NAME}.xcframework\"

}

开始制作

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
echo \"#####################\"

echo \"▸ Cleaning XCFramework output dir: \${FREAMEWORK_OUTPUT_DIR}\"

rm -rf \$FREAMEWORK_OUTPUT_DIR

\#### Make XCFramework

echo \"▸ Archive framework: \${FREAMEWORK_NAME}\"

archiveAllPlatforms \$FREAMEWORK_NAME

echo \"▸ Make framework: \${FREAMEWORK_NAME}.xcframework\"

makeXCFramework

\# Clean Build

rm -rf \"./Build\"

if \[ \${REVEAL_XCFRAMEWORK_IN_FINDER} = true \]; then

open \"\${FREAMEWORK_OUTPUT_DIR}/\"

fi

最后生成的xcframework:

对framework的命令处理

  • 查看framework包含的指令集
1
lipo -info /path/to/xxx.framework/xxx
  • 查看dynamic framework是否支持bitcode
1
otool -arch armv7 -l /path/to/xxx.framework/xxx \| grep \_\_bundle

如果包含 bitcode, 你会看到sectname __bundle信息。如果不包含,Terminal
输出为空

  • 查看static framework是否支持 bitcode
1
otool -arch armv7 -l /path/to/xxx.framework/xxx \| grep \_\_bitcode

同上输出信息

参考资料

[使用 Xcode 制作 Framework 与 XCFramework -
简书]