0%

如何将Swift Package编译成XCFramework

为什么要将Swift Package编译成XCframework?

这是一个困扰我很久的问题,问题产生的背景是多个Swift Package Manager创建的独立package,希望组合成一个SDK,这些package有些是第三方的,比如Alamofire、SwiftyJSON,也有自己写的package,自己写的package是依赖第三方package,现在想把自己写的package打包成XCFramework提供给别人使用,如何在不改动依赖情况下,将自己写的package和第三方的package都打包成XCframework?

简单理解一下需求,就是需要将SPM Package及其依赖的第三方SPM Package打包成XCFramework提供给别人使用。

有哪些解决方案?

方案1: swift-create-xcframework

swift-create-xcframeworkgithub上一个包装xcodebuild命令的命令行项目,通过这个命令行项目,你可以将自己写的SPM Package及其依赖的第三方SPM Package都打包成XCFramework项目地址

方案1示例

第一步:新建Package,然后编写Package.swift,设置第三方依赖

第二步:编写Package中的代码

编写代码,引用图片资源

第三步:在Package根目录,安装swift-create-xcframework

1
2
$ cd MyLibrary
$ brew install segment-integrations/formulae/swift-create-xcframework

第四步:查询Package中的Target

1
$ swift create-xcframework --list-products

第五步:编译打包Package中的全部target,多个target以空格分开

1
$ swift create-xcframework MyLibrary Alamofire

编译完成的截图

第六步:编译完成后会在Package根目录生成对应targetXCFramework文件

第七步:编写Demo测试生成后的XCFramework文件

编写代码测试xcframework

注意:swift-create-xcframework不是万能的,虽然它可以把自己写的SPM Package和Package依赖的第三方包都打包成XCFramework,但是如果自己的包和第三方的包里面引用了图片等资源,这些资源是不会打包进XCFramework的!!所以需要图片资源的可以继续看方案2。

方案2:自己编写脚本

第一步:编写打包脚本

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
#!/bin/bash

set -x
set -e

# Pass scheme name as the first argument to the script
NAME=$1

# Build the scheme for all platforms that we plan to support
for PLATFORM in "iOS" "iOS Simulator"; do

case $PLATFORM in
"iOS")
RELEASE_FOLDER="Release-iphoneos"
;;
"iOS Simulator")
RELEASE_FOLDER="Release-iphonesimulator"
;;
esac

ARCHIVE_PATH=$RELEASE_FOLDER

# Rewrite Package.swift so that it declaras dynamic libraries, since the approach does not work with static libraries
perl -i -p0e 's/type: .static,//g' Package.swift
perl -i -p0e 's/type: .dynamic,//g' Package.swift
perl -i -p0e 's/(library[^,]*,)/$1 type: .dynamic,/g' Package.swift

xcodebuild archive -workspace . -scheme $NAME \
-destination "generic/platform=$PLATFORM" \
-archivePath $ARCHIVE_PATH \
-derivedDataPath ".build" \
SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES

FRAMEWORK_PATH="$ARCHIVE_PATH.xcarchive/Products/usr/local/lib/$NAME.framework"
MODULES_PATH="$FRAMEWORK_PATH/Modules"
mkdir -p $MODULES_PATH

BUILD_PRODUCTS_PATH=".build/Build/Intermediates.noindex/ArchiveIntermediates/$NAME/BuildProductsPath"
RELEASE_PATH="$BUILD_PRODUCTS_PATH/$RELEASE_FOLDER"
SWIFT_MODULE_PATH="$RELEASE_PATH/$NAME.swiftmodule"
RESOURCES_BUNDLE_PATH="$RELEASE_PATH/${NAME}_${NAME}.bundle"

# Copy Swift modules
if [ -d $SWIFT_MODULE_PATH ]
then
cp -r $SWIFT_MODULE_PATH $MODULES_PATH
else
# In case there are no modules, assume C/ObjC library and create module map
echo "module $NAME { export * }" > $MODULES_PATH/module.modulemap
# TODO: Copy headers
fi

# Copy resources bundle, if exists
if [ -e $RESOURCES_BUNDLE_PATH ]
then
cp -r $RESOURCES_BUNDLE_PATH $FRAMEWORK_PATH
fi

done

xcodebuild -create-xcframework \
-framework Release-iphoneos.xcarchive/Products/usr/local/lib/$NAME.framework \
-framework Release-iphonesimulator.xcarchive/Products/usr/local/lib/$NAME.framework \
-output $NAME.xcframework

第二步:保存编译脚本,并放到Package根目录中,给脚本加上可执行权限,然后打包

1
2
$ chmod +x build.sh
$ ./build.sh MyLibrary

编译打包截图:

编译打包成功截图:

最终生成的XCFramework包含资源文件

注意:自己写脚本打包虽然可以将资源文件打包进XCFramework,但是资源文件被放在了一个单独的Bundle里面,与Xcode生成的XCFramework还是不太一样。并且,脚本无法将依赖的库也打包成XCFramework,所以具体采用哪一个方案还要结合自己的情况选择。目前看来两种解决方案都有缺陷,造成这种缺陷的原因是,Xcode目前没有支持将SPM Package打包成XCFramework的功能,所以才有这么多偏方,如果哪天Xcode支持了这个功能,那这两个方法都可以被抛弃了。

案例Demo

MyLibrary代码下载

参考链接

swift-create-xcframework

swift-create-xcframework(已不再维护)

How to build Swift Package as XCFramework

通过 Swift Package 制作二进制库

使用Swift Package Manager为二进制目标添加包依赖

向Swift Package中添加资源文件