4

关于 Swift Package Manager 的一些经验分享

 2 years ago
source link: https://blog.csdn.net/ByteDanceTech/article/details/120320419
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Swift Package Manager 是 Apple 为了弥补当前 iOS 开发中缺少官方组件库管理工具的产物。相较于其他组件管理控件,他的定义文件更加轻松易懂,使用起来也很 Magic,只需将源码放入对应的文件夹内,Xcode 就会自动生成工程文件,并生成编译目标产物所需要的相关配置。同时,SPM 与 Cocoapods 相互兼容,可以在特性上提供互补。

  • 项目地址:

    • https://github.com/apple/swift-package-manager

  • 相关文档:

    • https://developer.apple.com/documentation/swift_packages

这篇文章将主要介绍该组件管理器的现状,常见使用方法,和将来的一些思考。

开源组件使用情况

查看当前开源组件的 SPM 接入供应情况,不难发现几乎全部还在维护的框架都支持使用这种方式集成。大到微软的 APM SDK,小到界面 UI 组件,均有良好的兼容支持。下面列举一些可能会在后续开发中用到的组件。资源选自 https://github.com/ivanvorobei/awesome-ios。检测规则为是否在仓库主目录下存在 Package.swift 文件。

357fcd6f88223b84d6138efa1565e5b2.png

统计中,56%左右的框架已经适配了 SPM 接入,且已经开始出现如 MarkdownUI 等框架仅适配 SPM 的情况。

  • 简化的定义流程:将文件放入约定的目录内即可一键打包。

  • 简化的 SPM 版本管理:Xcode 会根据定义文件首行说明自动查找兼容的解决方案。

  • 简化的上手流程:不需要安装工具,也不需要命令行安装组件。

  • 良好的持续集成能力:在完成项目配置以后,xcodebuild 无缝衔接,自动拉仓。

  • 良好的兼容性:可与现有的大多数组件管理方案混用。

  • 良好的调试能力:断点快狠准。

  • 文档难找。

  • 使用远端仓库对网络要求非常高。

创建组件可以在 Xcode 中选择 Swift Package,也可以在命令行中写入 swift package init。命令行创建会将当前目录名称用作包名。

2de897f71e1f5a68e2624802d3772a43.png

基础的定义看起来长这个样子。别急,我们一行一行来看。

// swift-tools-version:5.5

请勿忽略本行,当打包编译出现工具链版本不匹配、 SDK 版本、系统 API 最低版本等问题时需要首先到这里排查可能存在的问题。

Swift Package 的定义稍微有一些绕,但是稍微解释一下也就明了了。

Targets

先看 targets,定义是 A target can define a module or a test suite. 翻译来说,就是一个 target 对应一个 clang module 或者 一个测试目标。一个 target 内只允许使用一类语言,比方说 Swift 或者 Objective-C/C/CPP。此处的 name 只对当前 package 可见,可以填写在任意一个 dependencies 内。Target 支持 binary Target,可使用 XCFramwork 或 .a .so 等二进制。

Products

再看 products,定义是 Products define the executables and libraries a package produces, and make them visible to other packages. 一个 product 可以包含多个 target,他们会被编译成产物提供给项目。如果其他项目依赖当前的 Swift Package,此处的 name 可以填写入其他 Package 的依赖需求内,一般对内不可用。最后来看一下 product 的几种类型。一般来说,常见的 .library 可 type 包含 .static (默认) 和 .dynamic。除开 .library 还有 .executable 可选,用于编译测试用二进制和 macOS 命令行工具。

资源文件

Swift Package 需要对每一个文件指明用途。代码文件会自动识别并编译打包,资源文件需要指定和说明。Swift Package 会为每一个 Package 生成一个 module 扩展,以便直接调用。使用命令行将项目文件 Package.swift 转换成 xcproj 则不会生成该模版定义文件。以下定义会在 Bundle 类内生成 .module 属性专门用于获取 Particles 文件夹内的资源。

f7ea2d9d27f3e8c9c11b27941f89959f.png

Swift Package 推荐使用原生目录结构,不推荐自定义 Path。

Swift Package 导出头文件有规定的位置,在当前 Source Path 内创建 include 会自动导出。

Swift Package 需要对每一个资源文件/文件夹显示声明,对通配符的适配存在 Bug。

当需要特定的文件目录组织的时候可以使用 符号连接 来链接目标文件。

总体来说 Swift Package 中一个 Target 对应一个 name,而 项目根目录/Sources/name 会作为当前 Target 的工作搜索路径。

XCFramework

关于编译产物,基础的 Swift Package 可以生成静态库、动态库,在这以后可以手动打包成 XCFramework。SPM 的打包工作流对 XCFramework 非常友好,可以参考下面这个脚本。

https://github.com/lexrus/LTMorphingLabel/blob/master/build_xcframework.sh

d46253321ab705113d55beb2a32b8abc.png

目前有针对 Package.swift 生成并编译 XCFrameowrk 的懒人工具,但是由于其依赖将项目转换成 xcproj 的编译方法,携带资源文件的 Swift Package 并不能用。

https://github.com/akkyie/XPM

0916a9f6c9f1b6d3ac924f9740816287.png

目前笔者有一个开源的私人项目使用了 SPM,可以拉下仓库来看一看。Xcode 在解析各种依赖方面并不稳定,所以项目采用的方案是将所有代码拉到本地并通过修改 dependencies 的方式采用本地解析集成。本地集成的方式非常稳定,而且最大程度的保证了你修改源码的能力。Swift 发展非常快,目前不推荐 url 直接集成远端仓库。

https://github.com/SailyTeam/Saily

在本地创建 xcworkspace 以后便可以直接将 Package.swift 中的 product 添加到项目的编译流程内。这里再次赞赏 Swift Package 的多元兼容,其中有一些库是纯 Objective-C 撰写的,可以一键无缝集成。

7589ab11356bb0ba6e309b9698796f03.png

本地集成的其他好处自然也包含 0 编译警告,遇到任何问题你都可以直接打断点到 Swift Package 的代码上。而 Cocoapod 经常不灵。关于编译警告,养眼准备!

98760be779a5690ad0fbd18ef146c919.png

其中可以重点关注几个混合编译的库的定义和 Fluent Icon 库的定义文件。其中就如上面描述的一样,include 文件会被自动导出给 Swift 使用。

c1e90ad22a3b8f3f3730387ef9879cbb.png8d4189d21e1d13a44651bf64c3fecfd6.png

Q: 我导入了 Swift Package 到项目,但无法 import

A: 请 command + shift + K 清理项目重新编译。Swift Package 有 module 缓存。

Q: 我的 include 指向上级目录的头文件,导出失败了

A: 请清理项目重新编译,有时需要重启 Xcode。

Q: 我在编译的时候指定了最低要求 iOS 13,为何 Swift Package 无法调用 API?

A: 请检查 Package.swift 是否有在 platform 内指定版本,如有请升级 swift-tools-version 定义行。

Q: 我的资源文件在添加 process 以后仍然有警告

A: 请使用文件夹名字或指定每一个文件的名字,通配符并不能很好的工作。

Q: 我在定义 Package.swift 的时候没有找到你说的这个几个字段

A: 请升级第一行的 swift-tools-version。

Q: 联网拉取 Swift Package 无法完成

A: 请考虑清除 ~/Library/Caches/org.swift.swiftpm/,并换个好一些的网络。如果依然失败请删除 Package.resolved 文件

如有问题可以添加评论补充。

本人是十分喜欢 Swift Package 的,本地集成方便快捷,也给我很大的权力让我所想落实到几乎不可能落实的上游仓库。配合 Swift Access Control,例如 module 内可访问的 internal 属性,很大程度上解决了写 App 后台的时候被 UI 意外调用造成的 crash,弥补上 Swift 没有 class-private 访问控制关键字的遗憾。调试可以直接打到代码上,速度也很快。如果能为 Package 提供 .patch 的扩展文件,再配合优化后的远端仓库,这将很有可能取代臃肿的 Cocoapod。pod 会修改编译目标的 xcconfig,而 Swift Package 通过提供 library 和 workspace 的集成方式,侵入性非常低。最后,Swift Package 的多平台编译的能力也非常好,UIKit 一次编写即可适配 iOS/iPadOS/tvOS/watchOS,编译配置 CI 只需要调用 xcodebuild 即可自动解析,如有缺失自动拉取,省时省力。个人项目我可能不会再碰 Cocoapods。

由于 Swift Package 在世界范围内的文档资源都非常稀缺,一旦出现问题,很难自行搜索解决,会需要参考非常多已有开源项目的代码,知识点非常零散。如果有一些想法,请考虑给我们留言或者写一写评论。

字节跳动 APM 中台目前致力于提升整个集团内全系产品的性能和稳定性表现,技术栈覆盖 iOS/Android/Flutter/Web/Hybrid/PC/游戏/小程序等,工作内容包括但不限于线上监控,线上运维,深度优化,线下防劣化等。长期期望为业界输出更多更有建设性的问题发现和深度优化手段。同时密切保持对业界前沿技术的关注,如 Swift async/await,SwiftUI,Swift Package Manager 等。

欢迎各位有识之士加入我们,一起为了“更快,更稳,更省,更有品质”的极致目标携手前行。我们在北京,深圳两地均有招聘需求,简历投递邮箱:[email protected] ;邮件标题:姓名 - 工作年限 - APM 中台 - 技术栈方向(如 iOS/Android/Web/后端)。

点个在看杀个 Bug ❤


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK