最近工作中的开发内容涉及到 Framework 的开发和使用,遇到一些问题,也算是解决了。
这里有一些基本的配置,记录一下,还有 简单的 XCFramework 配置使用。干货儿满满,收藏点赞?!
Framework 的配置
Build Setting
- Build Libraries for Distribution – YES
- Mach-O Type – Static Labriry (可选)
使用 .bundle
- 右键点击项目名。
- 选择 New Group
- 设置bundle名字:xxx.bundle
- 在这个目录汇中添加文件,可以添加代码用到的资源,路径就是
xxx.bundle/xx
。
Pods 三方库
在 Framework 中使用了的pod依赖,那么在引入Framework 的项目中也要加入相应的依赖,并且版本不能小于Framework 中的三方库版本。
开发 Framework
在 demo 中开发 framework,可以使用 Add Files 加入xcodeproj。
- General – Frameworks, Librarise, embed content – do not embed framework
- Build Phases – Target Dependencies 添加 framework
- Link Binary With Library – 添加 framework
构建
构建对应真机和模拟器的framework的文件,会在Xcode设置的目录下生成。
在这里 Xcode Setting – Loccasions – Derived Data 可以查看目录位置。在目录下的
/<Framework target name>/Build/Products/
路径,可以看到对应的文件夹。
真机和模拟器(iphonesimulator),在这个文件夹下边,就可以找到 XXX.framework。直接拖到项目中即可使用。
Target 配置
Xcode 项目引入 Static Labriry 类型 Framework 配置,
引入的项目中的 Target – General – Frameworks and Libraries – Do Not Embed
如果是 dynamic library,则以上设置 需要是 – Embed and Sign
和 Framework 相关的 XCFramework
XCFramework 适用于打包多平台的情况,其包内会有相关配置,当把生成的 XCFramework 导入到项目中,自动就能根据运行的平台进行判断构建,方便开发。从而替代以前使用 lipo
合并 framework 架构的情况,也少了一些 target 的 build setting 设置。
至于运行创建 XCFramework,也很简单,使用命令行脚本即可。
下面,就开始一个例子,来看怎样开发一个framework,以及如何多平台打包吧。
创建 framework 的 demo
demo 使用的是 Xcode 14.2
依次选择:File – New – Project…,选择 iOS – Framework
设置 framework 名称为 DemoFramework,然后选择一个存储位置。
创建好的 demoFramework 如下图,我们已经加入一个 HelloworldViewController
文件,是一个展示Helloworld 文字的 UIViewController。
另外,还把 Build Libraries for Distribution
设置为 YES
。这是根据前边的记录,打包 XCFramework 所用到的设置。
创建 调试Framework 的 Demo App
创建新的 App项目的话,大家已经很熟,不再详述。
创建好名为 DemoTestFramework 的项目后,右键点击左边目录的 DemoTestFramework Target – Add Files to “DemoTestFramework”,然后选择 DemoFramework,注意不要选择 Copy Item If needed
。
添加好之后,如下图所示,其中还添加了一些设置。
- 图中1区:这是添加后的项目目录。
- 图中2区:导入framework,然后让主视图控制继承 DemoFramework 中的
HelloworldViewController
。 - 图中3区:在 TARGETS – DemoTestFramework – General选项卡 – Framework, Libraries, and Embedded Content 中,添加了我们引入项目的 demo framework。
- 图中4区:在 TARGETS – DemoTestFramework – Build Phases – Target Dependencies 中,添加了 demo framework,这里是为了在修改 Framework 内容的同时,也会把 Framework 内容同时 build 一遍。
我们选择 14 pro模拟器 ,Run 一下,可以看到,项目已经成功运行,Demo Framework 也成功引入了 demo app。
现在,我们把 Demo Framework 中的 Label 内容,改为”Hello, Swift 6.0!”,然后 Run 一下。
运行成功,显示正确!?
这就说明,对于 Framework 的修改,已经成功被 Demo app 调用。
打包 Demo Framework 为 XCFramework
成功修改之后,就可以打包发布,以供其他项目引入使用。 这里直接上脚本,在脚本中解释吧。
#!/bin/sh
# 如果脚本放进Xcode的run script中运行,可以不设置这个PROJECT_NAME,
# 因为它同时也是 Xcode 项目的全局变量,用于获取项目名。
PROJECT_NAME=DemoFramework
# 我们framework的名字是和项目名一样的,如果不一样的的话,可以单独设置。
framework_name=$PROJECT_NAME
# 导出xcframework的路径
output_path=$HOME/${framework_name}_XCFramework
# 模拟器的存档位置
simulator_archive_path=$output_path/simulator.xcarchive
# 真机的存档位置
iOS_device_archive_path=$output_path/iOS.xcarchive
# 删除旧版,然后创建新版
rm -r $output_path
mkdir $output_path
# 打包模拟器
xcodebuild archive \
-scheme $framework_name \
-destination "generic/platform=iOS Simulator" \
-archivePath $simulator_archive_path \
SKIP_INSTALL=NO
# 打包真机
xcodebuild archive \
-scheme $framework_name \
-destination "generic/platform=iOS" \
-archivePath $iOS_device_archive_path \
SKIP_INSTALL=NO
# 创建 xcframework
xcodebuild -create-xcframework \
-framework $simulator_archive_path/Products/Library/Frameworks/$framework_name.framework \
-framework $iOS_device_archive_path/Products/Library/Frameworks/$framework_name.framework \
-output $output_path/$framework_name.xcframework
# 打包完成后,存档就失去作用,只作为中间打包过程使用。
rm -r $simulator_archive_path $iOS_device_archive_path
# 打开 XCFramework 所在的目录
open $output_path
可以直接将脚本内容放入Xcode的脚本中,注释掉项目名的设置,直接运行含有脚本的 Target 就行。
而我的脚本是以sh文件的形式放在项目根目录中的,如果你也是这样,直接使用命令运行即可,无需修改内容。
$ cd <DemoFramework 的项目路径>
$ sh DemoOutputXCFramework.sh
运行成功,会得到这样的文件结构。
.
└── DemoFramework.xcframework
├── Info.plist
├── ios-arm64
└── ios-arm64_x86_64-simulator
这就是创建在用户目录下的 XCFramework 文件内容**。**
导入项目使用
首先删除左边目录中对于 DemoFramework Target 的引用。
然后,直接将生成的 DemoFramework.xcframework 拖入 TARGETS – DemoTestFramework – General选项卡 – Framework, Libraries, and Embedded Content 中,如下图所示。
选择模拟器或真机,都Run一下,和预期一样,屏幕显示 “Hello, Swift6.0!”字样。
这样我们就为项目添加好了 XCFramework,而且模拟器和真机都可以运行。?
有pod 依赖的情况
上边的操作,都是基于无三方依赖的情况,如果我们的 Framework 中,添加了 Pods 依赖的话,情况又不太一样。
我们先在项目中添加 Pod依赖,在例子中,是加入了 SnapKit 这个布局库,过程省略,说起依赖,其实cocopods的简单操作也是个不错的主题,以后有时间可以总结一下。
由于采用了pods依赖,那么我们在 xcodebuild
命令中,需要加一条 -workspace $workspace_name
, 因为 pod项目运行的是 DemoFramework.xcworkspace, 我们实际 build的对象是指定的 workspace。脚本如下:
#!/bin/sh
# 如果脚本放进Xcode的run script中运行,可以不设置这个PROJECT_NAME,
# 因为它同时也是 Xcode 项目的全局变量,用于获取项目名。
PROJECT_NAME=DemoFramework
# 我们framework的名字是和项目名一样的,如果不一样的的话,可以单独设置。
framework_name=$PROJECT_NAME
# +++ 加入pods以后,指定 build 的 workspace
workspace_name="${PROJECT_NAME}.xcworkspace"
# 导出xcframework的路径
output_path=$HOME/${framework_name}_XCFramework
# 模拟器的存档位置
simulator_archive_path=$output_path/simulator.xcarchive
# 真机的存档位置
iOS_device_archive_path=$output_path/iOS.xcarchive
# 删除旧版,然后创建新版
rm -r $output_path
mkdir $output_path
# +++ 加入pods以后,打包模拟器
xcodebuild archive \
-workspace $workspace_name \
-scheme $framework_name \
-destination "generic/platform=iOS Simulator" \
-archivePath $simulator_archive_path \
SKIP_INSTALL=NO
# +++ 加入pods以后,打包真机
xcodebuild archive \
-workspace $workspace_name \
-scheme $framework_name \
-destination "generic/platform=iOS" \
-archivePath $iOS_device_archive_path \
SKIP_INSTALL=NO
# 创建 xcframework
xcodebuild -create-xcframework \
-framework $simulator_archive_path/Products/Library/Frameworks/$framework_name.framework \
-framework $iOS_device_archive_path/Products/Library/Frameworks/$framework_name.framework \
-output $output_path/$framework_name.xcframework
# 打包完成后,存档就失去作用,只作为中间打包过程使用。
rm -r $simulator_archive_path $iOS_device_archive_path
# 打开 XCFramework 所在的目录
open $output_path
如果是以脚本文件的形式放在项目根目录下,运行方式和上边没有pods依赖的方法一样。
得到的 XCFramework目录也是相同的。这部分上边已有详述。
然后我们加入项目 DemoFramework,使用真机运行一下,结果如预期。
但是在模拟器运行的时候,我遇到一个错误。
arm64-apple-ios-simulator.private 报错
/Users/youwei/Library/Developer/Xcode/DerivedData/DemoTestFramework-fiviywlmfwujstdvutfebnogtjuh/Build/Products/Debug-iphonesimulator/DemoFramework.framework/Modules/DemoFramework.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface:6:8
Could not find module 'SnapKit' for target 'arm64-apple-ios-simulator';
found: x86_64-apple-ios-simulator, at: /Users/youwei/Library/Developer/Xcode/DerivedData/DemoTestFramework-fiviywlmfwujstdvutfebnogtjuh/Build/Products/Debug-iphonesimulator/SnapKit/SnapKit.framework/Modules/SnapKit.swiftmodule
解决的方法,就是在项目中,对于模拟器的 build setting,排除 arm64架构即可。
在项目的 Build Settings – Excluded Architectures – Any iOS Simulator SDK – arm64
这是因为创建 XCFramework 时加入了arm64架构,本身这个应该是属于真机的。
# youwei @ 192 in ~/DemoFramework_XCFramework/DemoFramework.xcframework [17:45:05] C:1
$ lipo -info ios-arm64_x86_64-simulator/DemoFramework.framework/DemoFramework
Architectures in the fat file: ios-arm64_x86_64-simulator/DemoFramework.framework/DemoFra
模拟器 run 一下,好的,已经成功了。?
总结
XCFramework 是苹果出的新的二进制打包方式,感觉可以替代以前Framework打包合并平台架构的方法,可以同时支持多个平台,甚至还有 watch OS和 MacOS,而且打包过程简单,在日常工作中,掌握其使用还是有必要的。
最后,如果大家喜欢这篇文章的话,记得收藏,点赞,转发?!