6431fe7c0cf00028dc29c41038d60fbe
【WWDC22 10116】初见 CKTool JS

摘要:本文将带你了解如何使用 CKTool JS 自动化管理 iCloud 容器。展示如何配置 CKTool JS 来管理容器、修改记录以及操作数据。我们还将探讨如何将 CKTool JS 集成到自动化工作流程中。为了更好的理解,建议先了解 CloudKitJavaScriptnpm

本文基于 Session 10116 梳理。

作者:LabLawliet,在广州搬砖的 iOS 独立开发者,Swift 爱好者,GitHub

审核:JonyFang,方春,老司机技术社区核心成员,现于 BILIBILI 负责直播稳定性及性能优化相关工作

前言

本文将带你了解如何使用 CKTool JS 自动化管理 iCloud 容器。展示如何配置 CKTool JS 来管理容器、修改记录以及操作数据。我们还将探讨如何将 CKTool JS 集成到自动化工作流程中。为了更好的理解,建议先了解 CloudKitJavaScriptnpm

来自作者的温馨提示

CKTool JS 对于业务基于 CloudKit 的开发团队来说能够降低操作成本,并提供了拓展后台功能的可能性,建议仔细品读,Apple 也提供了示例代码,文末有贴上,有需要可以自取。

本次 WWDC 还有其他关于 CloudKit 的更新内容可以移步 WWDC22 10115/10119 - Optimize your use of Core Data and CloudKit / 优化 CoreData & CloudKit 实现,其中也有涉及到 CloudKit 控制台的一些更新,建议阅读。

一、 介绍

1.1 CloudKit 简介

CloudKit 是苹果为开发者提供的云端存储服务,可将应用程序的数据存储在 iCloud 容器中。通过在应用程序中使用 CloudKit,还可以让数据在不同设备上保持同步。

1.2 访问 iCloud 数据的方式

为了构建应用程序,你可以使用 Apple 平台上的 CloudKitWeb 上的 CloudKit JS 访问 iCloud 存储空间。为了实现自动化和工具化,Xcode 提供了 cktool 以在 macOS 上使用。现在,有了与 iCloud 交互的新方式 CKTool JS

1.3 CKTool JS 能做什么

CKTool JS 可以达到与 Xcode 13 中引入的 cktool 命令行工具相同的功能。实际上,CKTool JS 是用来实现 CloudKit 控制台中添加记录类型和查询记录等功能的。

通过 CKTool JS,可以管理应用程序的 CloudKit ContainerSchema。这是以前通过 JavaScript 无法做到的。

通过 CKTool JS 可以使用 recordName 查询特定记录,或者通过 查询条件 批量查询。同时也可以创建与更新记录。CKTool JSTypeScript 提供了严格的类型定义。这些类型定义启用了编译检查,并可在支持的 IDE 中进行代码补全,让编辑 CKTool JS 代码更容易。

1.4 npm 支持

CKTool JS 支持了对 Node.js 和浏览器的支持。CKTool JS 作为 npm 包进行分发,可以轻易的在 JavaScript 项目中集成。

这些 package 是以 @apple/cltool.* 开头的。这里的核心依赖库是 @apple/cltool.database。同时,为了与 iCloud 通讯,根据平台不同可选用 @apple/cktool.target.nodejs 或 @apple/cktool.target.browser

@apple/cltool.database 依赖了另外三个核心库 @apple/cktool.core@apple/cktool.api.base@apple/cktool.api.database。具体结构如下图所示:

1.5 访问授权

CKTool JS 要与 iCloud 通讯,首先需要授权。根据你需要的具体操作,你可能需要不同类型的授权:Management Token 或者 User Token。这两个 Token 都是从 CloudKit 控制台获取的。具体 Token 区别如下:

  • Management Token
    • 用于访问管理操作,并仅限于开发团队和用户。
    • 此类操作包括 Schema 的导入和导出、验证以及将 CloudKit Container 发布为生产。
  • User Token
    • 仅限于开发团队和特定容器,允许访问这些容器中的私有用户数据。

要了解如何获取这些授权 Token,请查看WWDC21 - Automate CloudKit tests with cktool and declarative schema

二、 配置 CKTool JS

2.1 CloudKit Schema

在开始配置 CKTool JS 之前,先简单了解下 CloudKit Schema。在 CloudKit 中,数据以结构化的方式进行存储。具有相同数据结构的数据以 Record 的形式存储在一起。RecordRecordType 的实例,RecordType 除了可以自定字段,也包含了一些自带的字段,如 recordNameID 唯一标识

例如这里以国家和货币为例该怎么去设计 RecordType 呢?下图是 CountriesCoins 各自的 RecordType

同时货币与国家是存在关联关系的,为了描述这种关系,这里我们可以通过将国家编码 isoCode 存储在 Coinscountry 字段中,使 CountriesCoins 形成如下图所示的关联关系:

RecordTypeRelationship 合起来就构成了 Schema

在应用程序功能迭代的过程中,我们的 Schema 也可能会不断的更新。

2.2 Container

Schema 决定了数据存储的结构,这些数据存储的地方就是 Container。每个 Container 都有一个唯一的 ID 并且是与 Developer Team 绑定的。和我们平时开发分测试生产环境一样,Container 也分为了 DevelopmentProduction 环境。在 Development 环境中完成了 Schema 的设计调试,就可以将 Schema 发布到 Production 环境了。

2.3 配置信息

为了使 CKTool JS 拥有权限访问正确的 CloudKit Container,我们需要进行一些参数配置如制定环境等。不同情况需要的信息在下图中列出了:

2.4 Node.js 配置示例

导入需要使用的相关依赖。并将需要的信息保存起来,如下面示例中的 securitydefaultArgs

构建参数

const { CKEnvironment } = require("@apple/cktool.database");

// 权限安全相关
const security = {
    "ManagementTokenAuth": "<YOUR_MANAGEMENT_TOKEN>",
    "UserTokenAuth": "<YOUR_USER_TOKEN>"
};

// 默认参数
const defaultArgs = {
    // Developer Team ID
    "teamId": "<YOUR_TEAM_ID>",
    // CloudKit Container ID
    "containerId": "<YOUR_CONTAINER_ID>",
    // 指定环境
    "environment": CKEnvironment.DEVELOPMENT
};

构建 configuration 和 API 对象

const { createConfiguration } = require("@apple/cktool.target.nodejs");
const { PromisesApi } = require("@apple/cktool.database");

const configuration = createConfiguration();
// 将 configuration 和 前面填写的 Token 数据传给 API 对象
const api = new PromisesApi({
    "configuration": configuration,
    "security": security
});

API 对象提供了异步访问 iCloud 的方法。

三、管理 Schema

在应用程序中,存储例如 2007 年发行的硬币之的信息。这枚硬币由铜和镍组成,价值 0.10 美元。在考虑了如何存储这些数据之后,决定将有关硬币成分的信息数据独立出来,即将硬币的铜百分比和镍的百分比分别存储在不同的 Record 中。

3.1 Schema File

现在定义好了 Schema 的结构,就可以按照一定的规则,创建一个文本文件 Schema File 来描述这些信息。后缀名 .ckdb

具体规则可以查看文档Integrating a Text-Based Schema into Your Workflow

Schema File 配置的 Schema 可以通过 CKTool JS 应用到 iCloud。在这之前,我们需要将当前 Development 环境的 Schema 恢复到 Production 环境的状态。调用 api.resetToProduction() 方法即可,记住要将在前面准备好的 defaultArgs 也传进去哦。如果当前 Development 环境中存在 Schema 不存在与 Production 环境,那么进行恢复操作后,这些 Schema 和数据会被删除。

注意,这是一个异步的方法,返回的是一个 PromisesApi 对象。

3.2 导入导出 Schema File

CKTool JS 提供了 exportSchemaimportSchema 方法来执行 Schema File 的导入导出操作。通过 exportSchema 可以导出当前 Schema 结构,通过 importSchema 可以将新的 Schema 结构上传到 CloudKit

下面是上传 Schema 的一个封装示例:

// Create a function to apply a schema
const { File } = require("@apple/cktool.target.nodejs");
const fs = require("fs/promises");
const path = require("path");

const importMySchema = async () => {
    // 指定 Schema File 文件路径
    const schemaPath = "<YOUR_SCHEMA_FILE>.ckdb";
    // 将文件写入 buffer
    const buffer = await fs.readFile(schemaPath);
    // 创建 File 对象
    const file = new File([buffer], schemaPath);
    // 使用前面配置的参数,上传 file
    await api.importSchema({ ...defaultArgs, "file": file });
}

/* 链式调用: 
 * 先同步 Production 环境的 Schema 到 Development 环境
 * 注意 这里 resetToProduction 方法是异步的
 * 完成后再调用 importMySchema 上传需要应用的 Schema File 
 */
api.resetToProduction(defaultArgs)
  .then(() => importMySchema());

四、 数据读写

4.1 类型校验

CKTool JS 在将数据发送到服务端之前,会在客户端进行类型与边界校验。如果类型错误或者超出了边界就会抛出异常。对于无法在 JavaScript 中原生支持的 Large number 可以使用 CKTool JS 类型来代替。例如:

  • 将数字强制转换为 CKTool JS Int64,使用 toInt64 函数。
  • 将数字强制转换为 Double 浮点值,使用 toDouble 函数。

在编写 TypeScript 时,如果不使用这些强制转换函数,编译器将报错。

4.2 构建 FieldValue

承接前文,继续以硬币为例。创建一枚 2007 年发行的硬币,将该值传递给 makeRecordFieldValue.int64 函数,以创建包含 Int64 的记录字段值。如果函数无法使用传入的值创建记录,就会抛出异常。

top Created with Sketch.