前言

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 文件。

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

一些优势

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

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

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

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

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

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

一些缺点

  • 文档难找。

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

使用方法

创建组件

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

Creating library package: Desktop
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/Desktop/Desktop.swift
Creating Tests/
Creating Tests/DesktopTests/
Creating Tests/DesktopTests/DesktopTests.swift

定义组件

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

// swift-tools-version:5.5

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

// swift-tools-version:5.5import PackageDescriptionlet package = Package(name: "MyLibrary",products: [.library(name: "MyLibrary", targets: ["MyLibrary"]),],dependencies: [ ],targets: [.target(name: "MyLibrary", dependencies: []),.testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),]
)

基础定义

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 命令行工具。

.library(name: "MorphingLabel", targets: ["MorphingLabel"]),
.library(name: "MorphingLabelDynamic", type: .dynamic, targets: ["MorphingLabel"]),.executable(name: "appdecrypt", targets: ["appdecrypt"])

资源文件

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

.target(name: "MorphingLabel",exclude: ["Info.plist", "tvOS-Info.plist"],resources: [ .process("Particles") ] // <-- 资源文件
),

目录结构

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

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

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

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

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

.
├── Package.swift        # 定义文件
├── README.md            # 可忽略
├── Sources              # 此文件夹内全部文件都需要定义 不然会报错
│  └── demo             # target demo 的默认目录
│    ├── Particles.     # 在 target 内声明为资源文件
│    │  └── fire.png   # 会自动打包成 bundle 拷贝并传递
│    ├── demo.swift     # target demo 的项目源代码
│    └── include        # 导出头文件
│      └── export.h     # 头文件# 在 Sources / target name 厘头的资源文件可在 Package.swift 内定义。
# 需要在 target 内添加 resources: [ .process("Particles") ]

其他说明

XCFramework

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

xcodebuild -create-xcframework \-framework "$BUILD_FOLDER/iOS.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \-framework "$BUILD_FOLDER/tvOS.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \-framework "$BUILD_FOLDER/Simulator.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \-framework "$BUILD_FOLDER/tvOSSimulator.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \-output Build/LTMorphingLabel.xcframework

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

product -> .library(name: "MorphinglabelXCFramework", targets: ["LTMorphingLabel"])
// 需要手动打包,此处仅提供名称给其他项目调用,依赖会使用二进制库
target -> .binaryTarget(name: "LTMorphingLabel",url: "https://github.com/lexrus/LTMorphingLabel/releases/download/0.9.3/LTMorphingLabel.xcframework.zip",checksum: "28a0ed8b7df12c763d45b7dde2aa41fd843984b79e6fbd3750f2fc1a6c247a13"
)

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

https://github.com/akkyie/XPM

一些实践

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

https://github.com/SailyTeam/Saily

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

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

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

常见问题

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 等。

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

点个在看杀个 Bug ❤

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

  1. Swift Package Manager 库制作

    Swift Package Manager 是苹果官方推出的库管理工具.类似 cocoapods. 准备事项: 一个需要封装的库 存放库的远程仓库,如 github.gitee.gitLab等 创建P ...

  2. combin14_使用SwiftUI,Combin和Swift Package Manager(SPM)构建复杂的模块化架构

    combin14 We should imagine a modular architecture like a modular building. We need to put pre-design ...

  3. [翻译练习] Swift Package Manager 重写 macOS 部署目标

    译自:oleb.net/blog/2017/0- Swift 3.1 修复了 Swift Package Manager 无法重写 MacOS 部署目标的 Bug. 当你在 macOS 上执行 swi ...

  4. Xcode Swift package manager 无法下载 GitHub 包问题

    解决方法: 使用终端进入项目根目录 复制并在终端运行终端代理命令 最后运行如下命令 xcodebuild -resolvePackageDependencies -scmProvider system ...

  5. Swift tips 017 - Speeding up Swift package tests

    代码截图 代码出处: Swift Tips 017 by John Sundell[1] 小笔记 这段代码在说什么 Swift Package Manager(Swift 包管理器,一般简称 Swif ...

  6. 【干货】Kaggle 数据挖掘比赛经验分享(mark 专业的数据建模过程)

    简介 Kaggle 于 2010 年创立,专注数据科学,机器学习竞赛的举办,是全球最大的数据科学社区和数据竞赛平台.笔者从 2013 年开始,陆续参加了多场 Kaggle上面举办的比赛,相继获得了 C ...

  7. Kaggle 数据挖掘比赛经验分享 (转载)

     [干货]Kaggle 数据挖掘比赛经验分享 (转载) 标签: 数据挖掘数据科学家机器学习kaggle 2017-05-21 19:25 99人阅读 评论(0) 收藏 举报 本文章已收录于: 分类 ...

  8. Unity MMORPG游戏优化经验分享

    今天由Unity技术支持工程师高岩,根据实际的技术支持工作经验积累,分享如何对Unity MMORPG游戏进行优化. 在优化Unity游戏时,我们一般从四个方面:CPU.GPU.内存.工程配置等入手, ...

  9. 清华大学 现代软件工程 - 实战经验分享

    在前两节课中, 老师给大家描绘了关于软件工程和创新的理论和美景. 那么在实践中, 我们的软件工程师是怎么创新的? 我们请两位清华的校友和大家聊聊他们自己做的项目, 也和大家探讨一下软件工程师的工作, ...

  10. kaggle账号_Kaggle 数据挖掘比赛经验分享

    文章发布于公号[数智物语] (ID:decision_engine),关注公号不错过每一篇干货. 来源 | 腾讯广告算法大赛 作者 | 陈成龙 Kaggle 于 2010 年创立,专注数据科学,机器学 ...

最新文章

  1. STM32 USB转串口驱动安装不成功出现黄色感叹号解决方法!
  2. nodejs与npm版本对应表
  3. catia圆管焊接焊接_CATIA焊接教程.ppt
  4. C 迭代器iterator的实现原理
  5. 利用D触发器和异或门实现两个IO解读电机编码器的计数值和方向
  6. Spring : Spring @Transactional-嵌套事物回滚
  7. C语言和数据结构_1
  8. 操作系统概念学习笔记 4 操作系统结构和操作简述
  9. Delphi的Hint(2)
  10. FFMpeg编译支持NVidia CODEC(成功)
  11. Spring源码阅读之在spring源码中创建一个gradle测试模块
  12. Java 全栈知识体系( PDF 可下载)
  13. 计算机应用历年高考真题,春季高考历年真题-2013年天津市春季高考计算机试卷...
  14. NIOS II 烧写epcs中遇到的问题
  15. mysql 整型最大值_在SQL中如何获取整数的最大值?
  16. 《人机交互技术》第二章 感知和认知基础
  17. python0基础 第三节
  18. mac版python3.7安装教程_M是什么意思_M的翻译_音标_读音_用法_例句_爱词霸在线词典...
  19. Ubuntu系统从机械硬盘迁移到SSD
  20. 险些“B轮死”的小猪短租,如何穿越了融资生死线

热门文章

  1. 【每天更新】2022年最新WordPress主题下载(2022-5-12)
  2. 迅雷Beta来了,这软件太实用了,磁力随心下
  3. python入门——快乐的数字
  4. linux键盘及终端快捷键
  5. 目前开发人工日多少钱_开发一个app多少钱啊?
  6. android arcgis缓存,ArcGis for android 加载tpk离线文件
  7. 浅谈localhost
  8. Raspberry 4B
  9. Freesurfer学习笔记——Introduction to Freesurfer Output(输出简介)
  10. Android中插件化实现的原理,宿主app运行插件中的类 (一)