Algunas experiencias compartidas sobre Swift Package Manager

prefacio

Swift Package Manager es el producto de Apple para compensar la falta de una herramienta oficial de administración de bibliotecas de componentes en el desarrollo actual de iOS. En comparación con otros controles de administración de componentes, su archivo de definición es más fácil de entender y también es muy mágico de usar. Simplemente coloque el código fuente en la carpeta correspondiente y Xcode generará automáticamente el archivo del proyecto y generará los productos necesarios para compilar el producto de destino configuración relacionada. Al mismo tiempo, SPM y Cocoapods son compatibles entre sí y pueden proporcionar funciones complementarias.

Este artículo presentará principalmente el estado actual del administrador de componentes, los métodos de uso común y algunas ideas futuras.

status quo

Uso de componentes de código abierto

Al observar el suministro actual de acceso a SPM de componentes de código abierto, no es difícil encontrar que casi todos los marcos que aún se mantienen admiten esta integración. Tan grandes como el SDK de APM de Microsoft, tan pequeños como los componentes de la interfaz de usuario, todos tienen un buen soporte de compatibilidad. A continuación se enumeran algunos componentes que se pueden utilizar en el desarrollo posterior. El recurso se selecciona de github.com/ivanvorobei... La regla de detección es si hay un archivo Package.swift en el directorio principal del repositorio.

Código abierto parcial-iOS-Framework Project-SPM-Adaptation Preview Table-Sheet12-1.png

Según las estadísticas, alrededor del 56% de los marcos se han adaptado para acceder a SPM, y ha comenzado a parecer que los marcos como MarkdownUI solo se adaptan a SPM.

algunas ventajas

  • Proceso de definición simplificado: coloque el archivo en el directorio acordado para empaquetarlo con un clic.
  • Gestión simplificada de versiones de SPM: Xcode encuentra automáticamente una solución compatible basada en la primera línea del archivo de definición.
  • Proceso de incorporación simplificado: no se requieren herramientas de instalación ni componentes de instalación de línea de comandos.
  • Buenas capacidades de integración continua: después de completar la configuración del proyecto, xcodebuild se conecta sin problemas y extrae automáticamente los almacenes.
  • Buena compatibilidad: se puede combinar con la mayoría de los esquemas de gestión de componentes existentes.
  • Buena capacidad de depuración: los puntos de interrupción son rápidos y precisos.

algunas desventajas

  • La documentación es difícil de encontrar.
  • El uso de almacenes remotos es muy exigente en la red.

Instrucciones

Crear componentes

创建组件可以在 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.5

import PackageDescription

let 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
复制代码

github.com/lexrus/LTMo…

imagen.png

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 并不能用。

github.com/akkyie/XPM

imagen.png

一些实践

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

github.com/SailyTeam/S…

在本地创建 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等。

Damos la bienvenida a todas las personas con perspicacia para que se unan a nosotros y trabajen juntos por el objetivo final de "más rápido, más estable, más económico y más calidad". Tenemos necesidades de reclutamiento en Beijing y Shenzhen. Correo electrónico de entrega de currículum: [email protected] ; asunto del correo electrónico: nombre - años de trabajo - oficina central de APM - dirección de la pila de tecnología (como iOS/Android/Web/backend).

Supongo que te gusta

Origin juejin.im/post/7007987863954391054
Recomendado
Clasificación