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:21)cktool 直接连接 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:07)cktool 也能写入数据。演讲用 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把项目中的.ckdbSchema 文件发送到 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 为每种记录类型创建的系统字段。description、pageCount、publishedOn、reviewStatus、title是应用自定义字段。TIMESTAMP、REFERENCE、STRING、INT64是字段数据类型。QUERYABLE给字段创建可查询索引,示例中___recordID和title都声明了该索引。// 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-schema、import-schema、create-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 management和save-token --type user完成授权,再把测试脚本按配置操作和数据操作分段组织。
关联 Session
- Meet CloudKit Console — 学习用 CloudKit Console 查看数据库、修改 Schema、部署容器变化。
- What’s new in CloudKit — 了解 WWDC21 CloudKit 的 async/await、加密字段和 Zone Sharing。
- Build apps that share data through CloudKit and Core Data — 继续学习 Core Data 与 CloudKit 共享数据的实现路径。
- Showcase app data in Spotlight — 了解 Core Data 数据如何进入 Spotlight 搜索索引。
评论
GitHub Issues · utterances