在组件的编码工作完成之后,就需要将代码进行公开托管以供其他开发者使用。作为全球最大的“码农交流”平台,Github 无疑是最好的选择。借助于它的 action,可以自动化地帮助我们完成很多重复性工作,在保证代码质量的同时减轻维护工作的压力。

定义 github 工作流

首先,在仓库根目录下,创建一个名为 .github 的文件夹。然后在 .github 下创建子目录 workflow,其中包含配置文件 build.yaml,用于定义所有的 action。一个典型的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
name: build

on: [push, pull_request] # 在什么时机下触发 action

jobs:
run-test:
runs-on: macos-12
strategy:
matrix:
destination: ["iOS Simulator,name=iPhone 14"]
swift-version: [5.0]
steps: # action 列表,依次执行
- name: Checkout
uses: actions/checkout@v3

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.1.2
bundler-cache: true

接下来就是完善 action 描述,声明 action 名称和需要运行的命令:

  • 添加 SwiftLint 检查:

    1
    2
    - name: Swift Lint
    run: brew install swiftlint && swiftlint lint --strict
  • 清除 Xcode 缓存:

    1
    2
    3
    - name: Clean Xcode DerivedData
    run: |
    rm -rf ~/Library/Developer/Xcode/DerivedData/
  • 安装工程依赖:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # ZonPlayer 工程是基于 pod 组件模版创建,同时通常情况下 Pods 会被 gitignore,因此需要进行 pod install。
    # 但是,Github Runner 默认是没有 cocoapods specs 缓存的,就会导致 pod install 非常耗时,所以需要自定义缓存。
    # ZonPlayer 最终没有添加这两个 action,后续会说明缘由。
    - name: Check Cache
    uses: actions/cache@v2
    id: cocoapods-cache
    with:
    path: Pods
    key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
    restore-keys: |
    ${{ runner.os }}-pods-
    - name: Pod Install
    if: steps.cocoapods-cache.outputs.cache-hit != 'true'
    run: bundle exec pod install
  • 进行单元测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 如果 Pods 被 gitignore 且未进行 pod install,在运行 xcodebuild 相关指令时会报错:
    #** BUILD FAILED **
    # Testing failed:
    # unable to open configuration settings file
    # unable to open configuration settings file
    # Testing cancelled because the build failed.
    # ** TEST FAILED **

    # 笔者最开始未注意这个点,导致 Github Action 有很多此类的失败记录,😅
    - name: Run Unit Tests
    env:
    DESTINATION: platform=${{ matrix.destination }}
    SWIFT_VERSION: ${{ matrix.swift-version }}
    # 命令1:借助 fastlane 进行单元测试
    # 命令2:Xcode 生成的单元测试覆盖率结果无法被 CodeCov 解析,需要借助命令工具 slather 将其解析为 XML 格式。需要在根目录添加一个 .slather.yml 配置文件,其中需要忽略 Example 相关代码。
    run: |
    bundle exec fastlane test_ci
    bundle exec slather
  • 上传单元测试覆盖率到 CodeCov

    1
    2
    3
    4
    - name: Upload coverage reports to Codecov
    uses: codecov/codecov-action@v3
    env:
    CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

这些行为还可以用来装饰我们的 README,看着那一个个漂亮的 Badge 就觉得专业和放心。而作为门户的 Logo 肯定是最需要上心的,所以我们选择将它交给 AI 来完成,这可得花费我们巨量的时间:1min 👀。

组件发布

Cocoapods

  • pod spec lint:校验你的 podspec,避免出现组件同名等问题。
  • pod trunk register mailAddress userName –verbose:注册你的邮箱通知。
  • pod trunk me:测试邮件收取
  • pod trunk push XXX.podspec:发布你的组件,完成后,会收到邮件通知。

Carthage

它不需要任何配置文件,而是采用默认规则:在工程中查找同名可见的 Shared Framework Scheme。在 Podfile 中加上以下代码即可:

1
2
3
use_frameworks!

install! 'cocoapods', :share_schemes_for_development_pods => ['ZonPlayer']

在 Xcode 15 (15A240d) 可能会运行报错 error while build iOS app in Xcode : Sandbox: rsync.samba (13105) deny(1) file-write-create,此时关闭 「Build Settings」-「User Script Sandboxing」 即可修复。当你提交完代码之后,执行 carthage update 时会发现始终报错未找到 shared scheme,而在本地运行时又没有什么问题。原因在于上述脚本是作用于 Pods.xcodeproj,而它是被 gitignore。最简单的方式就是让它被 git 跟踪,这样也省去了一开始提到的那两个 github action。

Swift Package Manager

在工程根目录添加名为 Package.swift 的配置文件即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
name: "ZonPlayer",
platforms: [.iOS(.v12)],
products: [
.library(name: "ZonPlayer", targets: ["ZonPlayer"]),
],
targets: [
.target(
name: "ZonPlayer",
path: "Sources"
)
]
)

总结

千淘万漉虽辛苦,吹尽狂沙始到金。前后几近两周里的闲暇时间,我们从头撸了一个就目前来看还算能用的播放器:ZonPlayer。工作是吃饭的把式,而终身学习让自己能为社会创造出或多或少的价值或许才是人生的最值得的追求。结果虽难言善美,但这一段经历却是职业生涯颇有意义的印记。旅途还在继续,不能停下脚步,得一直向前,我们下一站再见!