Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2022 Safari & WebSystem Services

WWDC22 · 16 min · Safari & Web / System Services

Meet CKTool JS

Discover how you can manage and automate your iCloud containers using CKTool JS. We’ll show you how to configure CKTool JS to manage your containers’ schemas, modify records with ease, and manipulate data on the fly. We’ll also explore how you can integrate CKTool JS into your automation and tooling workflows. To get the most out of this session, we recommend familiarity with CloudKit schemas, JavaScript, and npm.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 12 snippets

Create security and default arguments objects javascript · at 6:43 ↗
// Create security object and setup default args

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

const security = {
    "ManagementTokenAuth": "<YOUR_MANAGEMENT_TOKEN>",
    "UserTokenAuth": "<YOUR_USER_TOKEN>"
};

const defaultArgs = {
    "teamId": "<YOUR_TEAM_ID>",
    "containerId": "<YOUR_CONTAINER_ID>",
    "environment": CKEnvironment.DEVELOPMENT
};
Create configuration and API objects javascript · at 7:17 ↗
// Create configuration and API objects

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

const configuration = createConfiguration();
const api = new PromisesApi({
    "configuration": configuration,
    "security": security
});
Reset to production and import schema javascript · at 10:00 ↗
// 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 () => {
    const schemaPath = "<YOUR_SCHEMA_FILE>.ckdb";
    const buffer = await fs.readFile(schemaPath);
    const file = new File([buffer], schemaPath);
    await api.importSchema({ ...defaultArgs, "file": file });
}

// Chain the calls
api.resetToProduction(defaultArgs)
  .then(() => importMySchema());
Factory functions javascript · at 11:36 ↗
// Create fields with factory functions.

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

const value = makeRecordFieldValue.int64(2007);
Create database arguments object javascript · at 12:02 ↗
// Create a database arguments object.

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

const databaseArgs = {
    "containerID": "<YOUR_CONTAINER_ID>",
    "environment": CKEnvironment.DEVELOPMENT,
    "databaseType": CKDatabaseType.PRIVATE,
    "zoneName": "_defaultZone"
};
Query for records javascript · at 12:16 ↗
// Define helper function for querying records

const { CKDBQueryFilterType } = require("@apple/cktool.database");
const countryQueryRecordForCountryCode3 = async (countryCode3) => {
    const response = await api.queryRecords({
        ...databaseArgs,
        "body": {
            "query": {
                "recordType": "Countries",
                "filters": [{
                    "fieldName": "isoCode3",
                    "fieldValue": makeRecordFieldValue.string(countryCode3),
                    "type": CKDBQueryFilterType.EQUALS
                }]
            }
        }
    });
    return response.result.records[0];
}
Create field values javascript · at 12:58 ↗
// Define a helper function for creating field values

const {
    makeRecordFieldValue, CKDBRecordReferenceAction
} = require("@apple/cktool.database");

const makeCoinFieldValues = ({ countryRecordName, issueYear, nominalValue }) => ({
    "country": makeRecordFieldValue.reference({
        recordName: countryRecordName,
        action: CKDBRecordReferenceAction.DELETE_SELF
    }),
    "issueYear": makeRecordFieldValue.int64(issueYear),
    "nominalValue": makeRecordFieldValue.double(nominalValue)
});
Create a record javascript · at 13:26 ↗
// Define helper method for creating coins

const coinCreateRecord = async (fields) => {
    const response = await api.createRecord({
        ...databaseArgs,
        "body": {
            "recordType": "Coins",
            "fields": fields
        },
    });
    return response.result.record;
}
Call record creation helper method javascript · at 13:48 ↗
// Call coin creation method with field values

const countryRecord = await countryQueryRecordForCountryCode3("USA");

const coinRecord1 = await coinCreateRecord(
    makeCoinFieldValues({
        "countryRecordName": countryRecord.recordName,
        "issueYear": 2007,
        "nominalValue": 0.10
    })
);
Define update record helper function javascript · at 14:16 ↗
// Define helper method for updating coins.
// Note that recordChangeTag is required

const coinUpdate =
    async (recordName, recordChangeTag, fields) => {
        const response = await api.updateRecord({
            ...databaseArgs,
            "recordName": recordName,
            "body": {
                "recordType": "Coins",
                "recordChangeTag": recordChangeTag,
                "fields": fields
            }
        });
        return response.result.record;
    }
Update a record with field values javascript · at 14:44 ↗
// Call coin updating method with field values.
// Note that the recordChangeTag of the record
// to update is passed to the coin update function.

const countryRecord = await countryQueryRecordForCountryCode3("USA");
const updatedCoinRecord1 = await coinUpdate(
    coinRecord1.recordName,
    coinRecord1.recordChangeTag,
    makeCoinFieldValues({
        "countryRecordName": countryRecord.recordName,
        "issueYear": 2010,
        "nominalValue": 0.10
    });
);
Delete a record javascript · at 14:57 ↗
// Deleting a record

await api.deleteRecord({
    ...databaseArgs,
   "recordName": coinRecord1.recordName
});

Resources