WWDC Quick Look 💓 By SwiftGGTeam
Automate CloudKit tests with cktool and declarative schema

Automate CloudKit tests with cktool and declarative schema

观看原视频

Highlight

Apple 在 Xcode 13 中加入 cktool 和 CloudKit 声明式 Schema 语言,让开发者能在测试前自动重置 CloudKit 容器、导入 Schema、写入样本数据,解决云端集成测试环境不稳定的问题。

核心内容

CloudKit 应用的集成测试,最怕服务器状态漂移。

本地代码改了数据模型,CloudKit 容器里的 Schema 可能还停在旧版本。测试第一次跑会创建数据,第二次跑时又会读到上一次留下的记录。开发者最后花时间清理控制台、同步字段、重建样本数据,测试本身反而变成次要工作。

Apple 在 WWDC21 给出两个工具。第一个是 cktool,随 Xcode 13 安装,通过 xcrun 从命令行访问 CloudKit。它能导入和导出 Schema,也能创建和查询记录。第二个是 CloudKit schema language(CloudKit Schema 语言),它把容器结构写成文本文件,可以和应用代码一起提交到版本控制。

这样,CloudKit 测试环境可以由脚本准备。测试开始前,脚本先清空开发环境,再导入项目里的 Schema 文件,最后写入固定样本数据。Xcode 的 Test pre-action(测试前置动作)会在测试运行前执行这段脚本。测试每次面对同一个容器状态,失败原因更容易定位。

从控制台操作变成脚本操作

过去,CloudKit Console 适合人工查看和修改容器。它能完成 Schema 管理,但不适合放进持续集成流程。团队成员也很难从 Git diff 里看到 CloudKit Schema 的变化。

cktool 把这些操作放到命令行。导出 Schema 是一个命令,导入 Schema 也是一个命令。创建测试记录可以直接传 JSON。开发者不需要打开浏览器,也不需要在测试前手动点控制台。

从隐式服务器状态变成文本文件

Schema 语言把记录类型、字段、索引和权限写在 .ckdb 文件里。这个文件放在项目目录,和 Swift 代码一起审查。

当应用的数据模型变化时,Schema 变化也会进入同一次提交。测试脚本导入的就是这个文件。服务器状态来自仓库里的文本声明,团队不再依赖某个人记得去控制台改字段。

详细内容

授权 cktool

02:21cktool 直接连接 CloudKit 服务器,使用前需要授权。CloudKit 为它提供两类 token。Management token(管理 token)用于容器配置,比如导入和导出 Schema。User token(用户 token)用于向应用容器的私有数据库或公共数据库写入数据。

xcrun cktool save-token --type management

xcrun cktool save-token --type user

xcrun cktool get-teams

关键点:

  • xcrun cktool 调用 Xcode 13 安装的 CloudKit 命令行工具。
  • save-token --type management 保存管理 token,用于 Schema 导入、导出等配置操作。
  • save-token --type user 保存用户 token,用于访问容器数据。
  • token 会安全存入 macOS Keychain。
  • get-teams 返回当前开发者账号所属的 Apple Developer 团队列表,后续命令需要使用 team id。

导出 CloudKit Schema

03:45)如果容器里已经有开发中的 Schema,可以先导出成文件,再提交到源码仓库。演讲中导出的文件名是 schema.ckdb

xcrun cktool export-schema \
  --team-id XYZ1234567 \
  --container-id iCloud.com.WWDC21.Example \
  --environment development \
  --output-file schema.ckdb

关键点:

  • export-schema 从 CloudKit 容器导出 Schema。
  • --team-id XYZ1234567 指定 Apple Developer 团队。
  • --container-id iCloud.com.WWDC21.Example 指定要操作的 CloudKit container(容器)。
  • --environment development 选择开发环境。演讲强调开发环境中的记录类型和自定义字段可以添加、删除。
  • --output-file schema.ckdb 把导出的 Schema 写入文本文件,便于纳入版本控制。

创建测试记录

04:07cktool 也能写入数据。演讲用 JSON 表示一个 Book 记录,然后把它创建到 public database(公共数据库)。

xcrun cktool create-record \
  --team-id XYZ1234567 \
  --container-id iCloud.com.WWDC21.Example \
  --environment development \
  --database-type public \
  --record-type Book \
  --fields-json '{
       "title": { "type": "stringType", "value": "Treasure Island" },
       "pageCount": { "type": "int64Type", "value": 304 }
    }'

关键点:

  • create-record 在 CloudKit 服务器创建一条记录。
  • --database-type public 指定写入公共数据库。
  • --record-type Book 指定记录类型,必须和容器 Schema 中的记录类型一致。
  • --fields-json 用 JSON 描述字段值。
  • title 的类型是 stringType,值是 Treasure Island
  • pageCount 的类型是 int64Type,值是 304

把 CloudKit 准备步骤接入 Xcode 测试

05:05)演讲把三类操作放进 Xcode scheme 的 Test pre-action:重置容器、导入项目里的 Schema 文件、创建样本记录。cktool 命令同步执行,前一个命令失败会阻止后续命令继续运行。

xcrun cktool reset-schema \
    --team-id XYZ1234567 \
    --container-id iCloud.com.WWDC21.Example

xcrun cktool import-schema \
    --team-id XYZ1234567 \
    --container-id iCloud.com.WWDC21.Example \
    --environment development \
    --file $PROJECT_DIR/Example/CloudKitSchema.ckdb

xcrun cktool create-record \
    --team-id XYZ1234567 \
    --container-id iCloud.com.WWDC21.Example \
    --environment development \
    --database-type public \
    --record-type Book \
    --fields-json '{
       "title": { "type": "stringType", "value": "Great Expectations" },
       "pageCount": { "type": "int64Type", "value": 544 },
       "description": { "type": "stringType", "value": "Depiction of the education of an orphan nicknamed Pip" },
       "publishedOn": { "type": "timestampType", "value": "1860-12-01T03:23:07.415Z" },
       "reviewStatus": { "type": "int64Type", "value": 1 }
    }'

关键点:

  • reset-schema 重置指定 CloudKit 容器的 Schema,适合测试前清理开发环境。
  • import-schema 把项目中的 .ckdb Schema 文件发送到 CloudKit 服务器。
  • $PROJECT_DIR/Example/CloudKitSchema.ckdb 使用 Xcode build setting 定位项目内的 Schema 文件。
  • 第二个 create-record 写入更完整的 Book 样本记录。
  • publishedOn 使用 timestampType,值是 ISO 时间字符串。
  • reviewStatus 使用 int64Type,值是 1
  • 这段脚本放入 Test pre-action 后,每次运行测试前都会准备同一套 CloudKit 状态。

阅读 Schema 语言文件

05:51)Schema 文件描述 CloudKit 的记录类型、字段、索引和安全角色。演讲中的示例定义了 Book 记录类型。

DEFINE SCHEMA
     RECORD TYPE Book (
        "___createTime" TIMESTAMP,
        "___createdBy"  REFERENCE,
        "___etag"       STRING,
        "___modTime"    TIMESTAMP,
        "___modifiedBy" REFERENCE,
        "___recordID"   REFERENCE QUERYABLE,
        description     STRING,
        pageCount       INT64,
        publishedOn     TIMESTAMP,
        reviewStatus    INT64,
        // A single-line comment, for humans
        title           STRING QUERYABLE,
        GRANT WRITE TO "_creator",
        GRANT CREATE TO "_icloud",
        GRANT READ TO "_world"
     );

关键点:

  • DEFINE SCHEMA 开始一个 CloudKit Schema 声明。
  • RECORD TYPE Book 定义一个名为 Book 的记录类型。
  • "___createTime""___createdBy""___etag""___modTime""___modifiedBy""___recordID" 是 CloudKit 为每种记录类型创建的系统字段。
  • descriptionpageCountpublishedOnreviewStatustitle 是应用自定义字段。
  • TIMESTAMPREFERENCESTRINGINT64 是字段数据类型。
  • QUERYABLE 给字段创建可查询索引,示例中 ___recordIDtitle 都声明了该索引。
  • // A single-line comment, for humans 是给团队阅读的注释,CloudKit 服务器处理文件时会忽略注释。
  • GRANT WRITE TO "_creator" 允许记录创建者写入。
  • GRANT CREATE TO "_icloud" 允许已认证 iCloud 用户创建记录。
  • GRANT READ TO "_world" 允许所有用户读取。

记住生产环境的 Schema 演进规则

07:14)Schema 文件让修改更快,但 CloudKit 的生产环境规则没有改变。开发环境中可以添加和删除记录类型,也可以添加和删除自定义字段。记录类型一旦提升到生产环境,就不能删除或重命名。已经提升到生产环境的自定义字段,也不能删除或重命名。

Development environment:
- Add and remove record types
- Add and remove custom fields

Production environment:
- Promote new record types
- Add new fields to existing record types
- Cannot delete or rename promoted record types
- Cannot delete or rename promoted custom fields
- Can add and remove indexes
- Can modify security role settings

关键点:

  • 开发环境适合快速迭代 Schema。
  • 生产环境保留旧记录类型和字段,是为了让 CloudKit 服务器仍能理解旧版本应用使用的数据。
  • 破坏性 Schema 变更可以出现在开发环境,不能提升到生产环境。
  • 索引和安全角色设置在生产环境中仍可修改。

核心启发

  • 做什么:给 CloudKit App 增加一套可重复运行的集成测试环境。 为什么值得做cktool 能在测试前导入 Schema 并创建样本数据,避免测试依赖手动准备的服务器状态。 怎么开始:把 reset-schemaimport-schemacreate-record 写入 Xcode scheme 的 Test pre-action,Schema 文件路径使用 $PROJECT_DIR

  • 做什么:把 CloudKit Schema 纳入 Git code review。 为什么值得做.ckdb 文件能描述记录类型、字段、索引和权限,团队可以像审查 Swift 代码一样审查数据模型变化。 怎么开始:先用 xcrun cktool export-schema --environment development --output-file schema.ckdb 导出现有 Schema,再把文件加入项目。

  • 做什么:为 UI 测试准备固定的云端样本数据。 为什么值得做create-record 支持用 JSON 写入 public database,测试能稳定读到同一组记录。 怎么开始:为每个测试场景写一组 --fields-json,在测试前置脚本中逐条创建需要的 CloudKit 记录。

  • 做什么:给团队建立 CloudKit Schema 迁移检查清单。 为什么值得做:生产环境不能删除或重命名已提升的记录类型和自定义字段,提前检查能减少部署失败。 怎么开始:在 PR 模板中加入 .ckdb 变更项,检查是否包含破坏性变更、索引变更和安全角色变更。

  • 做什么:为持续集成拆分管理 token 和用户 token。 为什么值得做:Management token 只处理容器配置,User token 才处理数据写入,权限边界更清楚。 怎么开始:在本机先用 save-token --type managementsave-token --type user 完成授权,再把测试脚本按配置操作和数据操作分段组织。

关联 Session

评论

GitHub Issues · utterances