ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
CKTool JSの紹介
CKTool JSを使用して、Cloudコンテナを管理および自動化する方法をご覧ください。CKTool JSを設定してコンテナのスキーマを管理したり、レコードを簡単に変更したり、データをオンザフライで操作したりする方法を紹介します。また、CKTool JSを自動化やツール使用のワークフローに統合する方法についても解説します。 このセッションを最大限に活用するには、CloudKitスキーマ、JavaScript、npmに関する知識を習得しておくとよいでしょう。
リソース
関連ビデオ
WWDC22
WWDC21
-
ダウンロード
CloudKitチームのエンジニアKentです CloudKitにアクセスするための 新しいライブラリをご紹介します まずライブラリの設定方法を説明します そして スキーマを管理する方法と CKTool JSでユーザーデータに アクセスする方法です 始めましょう!
CloudKitは Appのデータをコンテナ内の iCloudに保存できる永続化テクノロジーです CloudKitを使用することで デバイス間およびWeb上で データを最新の状態に保つこともできます Appを構築するためにAppleプラットフォームの CloudKitフレームワーク WebのCloudKitJSを使って iCloudストレージにアクセスできます 自動化とツールを実装するためにXcodeは CKTool JSを使用して変更を自動化し iCloudと対話できます
CKTool JSはcktoolコマンドライン ユーティリティと同じ操作を実行でき 同様のユースケースをサポートします CKTool JSはCloudKit Consoleのレコードタイプ 追加やレコードの問い合わせ などの機能を実装します
CKTool JSを使用するとAppコンテナを管理し スキーマのリセットや適用などの スキーマ操作を実行することができます これまでJavaScriptでは実行できませんでした
CKTool JSでは 既存のレコードを一意の識別子で または複雑なクエリを通じて 取得することができます 新しいレコードを作成したり更新もできます CKTool JSはTypeScriptの 厳密な型定義が付属します この型定義でクライアント ライブラリの誤った使用に フラグを立てるコンパイル時 のチェックが可能になり サポートしているIDEでの コード補完が可能になります このためCKToolJSコードの 編集が簡単になります
さらにこの新しいライブラリには Node.jsとブラウザの両方が サポートされています CKTool JSはnpmパッケージの セットとして配布され JavaScriptのビルドパイプラインに統合します ツリーシェイクやバンドル等 の機能が可能になります パッケージのリリース履歴は npmから透過的に入手して パッケージの更新を追跡することも可能です
次はCKToolJSディストリビューションです これらのパッケージは@appleスコープ内にあり
名前の先頭にcktool dotを 使用する規則に従います 主なパッケージはcktooldotデータベースです iCloudとの通信を有効にして ターゲットプラットフォーム用 にもう1つのパッケージ Node.js用のcktool dot Target dot nodejs
またはWebブラウザ用の Cktool dot target dotブラウザも 使用する必要があります CKToolJSは直接通信するため iCloudでは最初に認証します 呼び出したい操作に応じて管理者トークン またはユーザートークンが必要になります どちらも CloudKit Consoleから取得可能です
管理トークンは管理操作に アクセスするためのもので チームとユーザーを対象にしています スキーマのインポートとエクスポートの有効化 スキーマの検証 コンテナの 実稼働リセットが含まれます ユーザートークンはチームとコンテナ対象で プライベートユーザーデータ アクセスを可能にします 認証トークンを取得する方法と CloudKitとの継続的 インテグレーションについては WWDC21「cktoolと宣言型スキーマによる CloudKitテストの自動化」をご覧ください
スクリプトでCKToolJSを 使用する場合は 最初に使用するように構成します CKTool JSの構成に入る前に CloudKitスキーマを構成する ものについて説明します CloudKitでは データは 構造化されて保存されます 同種の値のIDataはレコード として一緒に格納されます レコードはレコードタイプの インスタンスであり レコードタイプが記述する レコードのプロパティは フィールドと呼ばれます CloudKitはユーザー定義フィールドに加えて recordNameなどのシステム フィールドを追加します コインコレクションAppの例を使用します 国のコレクション保存のため 記述用のレコードタイプがあります 名前とISOコードを保存して レコードの種類に「国」と名前をつけます ISOコードは国を識別するため レコードタイプに含めることが重要です
名前とともに保存するために 国タイプのレコードをいくつか作成します
特定の国のコインのレコードタイプもあり それらを相互に関連付けたいと思います コインからその国までの関係を格納します
これらを組み合わせてスキーマを作成します これらの要素の現在の状態が私のスキーマの 現在のバージョンであると 考えることができます スキーマは進化しAppの寿命が尽きるまで いくつかのバージョンに別れるでしょう
スキーマはiCloudに保存 したいデータの構造を記述 コンテナはそのデータが保存される場所です コンテナには開発チームに 関連づける識別子があります CloudKitを使用する際に2つの環境があります 開発環境はユーザーを混乱させることなく 変更を加えるための安全な場所です ここでスキーマの変更をテスト 開発する必要があります ユーザーがAppを操作すると 本番環境を操作することになります ここにはAppのライブデータが含まれています CloudKitのデータ保存方法を確認したところで CKTool JSの設定方法について説明します. CKTool JSはiCloudと通信するため 適切なコンテナの操作方法を認識し スクリプトが許可されるように 情報を収集する必要があります
作業するコンテナのチームIDと コンテナIDが必要になります スキーマを操作するには 管理トークン スクリプトが データにアクセスする時は ユーザートークンが必要です 値はすべてCloudKitコンソールから取得します スクリプトを実行する 環境 開発 または本番環境を 指定する必要があります 今後は開発を例に挙げて説明します CKTool JSを設定するときは これらの値が必要です 私はNode.jsのスクリプトを書いています CKTool JSからオブジェクトや 関数をインポートします CommonJSのrequire文を使い シンボルをインポートします 設定情報を集めたら 情報を保持するオブジェクトを作成します 認証トークンを格納するため 管理トークンと 可能なら ユーザートークンを格納する オブジェクトを作成します teamId containerId environmentはCKToolJSに 渡す共通の値で保持する オブジェクトを作成できます createConfiguration関数を使って CKToolJSに Configurationオブジェクトを インスタンス化します createConfigurationは プラットフォーム固有です Node.jsはターゲットパッケージから インポートした関数であるため 適切な構成が返されます 先に宣言した構成オブジェクトと セキュリティオブジェクトを渡し APIオブジェクトを初期化します これにはiCloudと通信する 非同期メソッドが含まれます スクリプトでCKToolJSを 使う手順が完了しました CKToolJSを使用してコンテナの スキーマを管理する方法について学びましょう 2007年に発行されたアメリカの 10セント硬貨などの 情報を保存したいと思います この硬貨は銅とニッケルで構成されており 刻印されている金額は米ドルの1/10です データの保存方法を考えた結果 コインの構成に関する情報を コインに関する 他の詳細とは別のレコードとして 保存することにしました そのため10セント硬貨の銅の割合と 硬貨のニッケルの割合を 別々のレコードに保存します コンテナのスキーマに必要な 2つのレコードタイプを特定しました 貨幣は 国名 発行年 額面金額を記録しています コンポーネントレコードタイプは コインの名前と説明 コインに含まれる材料とその割合を保存します Appのスキーマが決まったので CloudKit Schema Languageで テキストファイルを作ります スキーマファイルには .ckdb 拡張子を使用します
CloudKit Schema Languageについては 「Integrating a Workflow Text-Based Schema into Your Workflow」を参照してください
コンテナ用スキーマファイル はCKTool JSで適用します 新しいスキーマを適用する前に コンテナの開発用スキーマをリセットして 本番用のスキーマと一致させます resetToProduction メソッド で行うことができます メソッドを呼び出すには先ほど宣言した defaultArgsオブジェクトを渡します スキーマが本番環境でないと 全レコードタイプ削除です 本番環境ではない場合は開発用スキーマを 本番環境の状態に戻すことになります 非同期呼び出しなので プロミスオブジェクトを返す
ことに注意してくださいCKTool JSはコンテナの スキーマをエクスポートインポートできます exportSchemaとimportSchemaを使用すると コンテナの観点から名前が付けられます コンテナからエクスポートするスキーマを exportSchemaでダウンロードし コンテナにインポートするスキーマを importSchemaでアップロードします これらを組み合わせることで スキーマの進化を管理することができます
コンテナにスキーマを適用 するヘルプ関数を作成します CKTool JSからFileオブジェクトをインポートし Node.jsからfsモジュールと pathモジュールをインポート 以下のような非同期関数を定義してください スキーマファイルをNode.js のバッファに読み込みます アップロード用のCKTool JS Fileのインスタンスを作り importSchema を使いサーバー にファイルをアップします 宣言したdefaultArgsオブジェクトを importSchemaに渡している ことに注意してください これで組み立てることができます resetToProductionと スキーマのインポートに使う ヘルプ関数は非同期なので 正しい順序で実行します そのために約束事を連鎖させます エラーが発生した場合は拒否されます CKTool JSが持つ管理機能だけでなく データの読み書きを行うこともできます CKTool JSレコードで使うフィールド値は サーバーに送信される前にクライアント側で タイプと範囲をチェックされます 値が正しい種類の値でないか 値の許容範囲外である場合 例外がスローされます JavaScriptで表現できない大きな数の場合 代わりに使用されるCKToolJSタイプがあります 番号をCKTool JS Int64に強制変換するには toInt64関数を使用します 数値を倍精度浮動小数点値に強制変換するには toDouble関数を使用します TypeScriptを作成中に強制関数を使わない場合 コンパイラは誤った値型 の使用にフラグを立てます
CKTool JSレコードのフィールド値は フィールド値ファクトリ 関数を使用して作成します 2007年に発行されたコインの場合 Int64を含むレコードフィールド値を作るために makeRecordFieldValuedotint 64ファクトリ関数に渡します ファクトリ関数が渡された値 からレコードフィールド値を 作成できない場合例外がスローされます
レコードを処理するメソッド に送信する共通の値を 保持するオブジェクトを作成しました containerId environment databaseType zoneNameを databaseArgsオブジェクトに含めます queryRecordsメソッドでレコード照会をします 簡単にするために 固有の3文字のISOコードに 一致する国を見つけるヘルパー関数を作成します クエリを含む本文に加えて databaseArgsオブジェクトの コンテンツを渡します クエリオブジェクトにはrecordType値と単一の フィルターオブジェクトを指定しています filterオブジェクトは国のisoCode3が 関数が求めているものと 等しいクエリを記述します 成功した場合 見つかった レコードのコレクションは 応答ドット結果ドットレコード プロパティにあります このコレクションから最初の オブジェクトを返します
createRecordが使用できる フィールド値に変換するために ヘルパー関数makeCoinFieldValuesを使います コインの生のプロパティのうち フィールド値に変換するには レコードフィールドファクトリ関数を使います 国名フィールドについては参照を作成します 国別レコード名を使ってコインレコードから 対応する国のレコードを参照します
コインレコードフィールド値を 取得して createRecord リクエストをサーバーに送る ヘルパー関数を作成します この関数では 以前に宣言したdatabaseArgsの コンテンツと本体を渡します 本文ディクショナリには recordTypeとィールドの値が含まれています 成功したら応答ドット結果 ドットレコードが返ります
ヘルパー関数を呼び出す前に このコインから参照される 正しい国のレコードを取得する必要があります 前に定義した国クエリ関数を使用します makeCoinFieldValuesヘルパー 関数で作成したフィールド値 ディクショナリを渡してcoinCreateRecordを 呼び出します 生のコインの値は その ヘルパー関数に渡されます 非同期でレコードが作成され 新しいレコードが返されます
更新するにはupdateRecord メソッドを使用します レコード名とこのヘルパーに 渡されたフィールドに一致する コインを更新するヘルパー関数を作成します databaseArgsオブジェクト recordName レコードタイプと 本文の内容を使用して updateRecordを呼び出します 成功すると 更新レコードは ヘルパー関数から返される 応答ドット結果ドットレコード プロパティに含まれます
コインレコードを更新する為 ヘルパー関数を呼び出し レコード名とフィールド値を渡して更新します フィールド値はmakeCoin FieldValuesで作成します
レコード削除には 非同期 deleteRecordを呼び出します databaseArgsオブジェクトの内容と 削除するrecordNameを渡します CKToolJSを楽しんでいただけたでしょうか ご自分でも試してみてください 自動化とツールの目的でCKToolJSを構成します スキーマをリセットしてインポートし JavaScriptを使用してデータを読み書きします 継続的インテグレーション シナリオのCKToolJS使用は GitHubのCloudKitサンプル リポジトリをご確認ください 詳細はdeveloper.apple.comの CKToolJSをご覧ください ご参加ありがとうございます WWDC22をお楽しみください
-
-
6:43 - Create security and default arguments objects
// 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 };
-
7:17 - Create configuration and API objects
// 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 });
-
10:00 - Reset to production and import 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 () => { 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());
-
11:36 - Factory functions
// Create fields with factory functions. const { makeRecordFieldValue } = require("@apple/cktool.database"); const value = makeRecordFieldValue.int64(2007);
-
12:02 - Create database arguments object
// 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" };
-
12:16 - Query for records
// 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]; }
-
12:58 - Create field values
// 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) });
-
13:26 - Create a record
// 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; }
-
13:48 - Call record creation helper method
// 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 }) );
-
14:16 - Define update record helper function
// 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; }
-
14:44 - Update a record with field values
// 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 }); );
-
14:57 - Delete a record
// Deleting a record await api.deleteRecord({ ...databaseArgs, "recordName": coinRecord1.recordName });
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。