ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
CloudKit共有の最大限の活用
AppでCloudKitを使ってユーザー間でレコードを共有する方法を確認します。Appを使ったユーザー間のコラボレーションを促進する方法や、Appleのフレームワークを使って共同作業をサポートする方法を確認します。共有を作成して管理する方法、パブリックアクセスの権限といった共有オプション、iOS 15やmacOS Montereyでゾーン共有を使ってデータのレコードゾーン全体を共有する方法を紹介します。このセッションを十分理解できるよう、事前にCloudKitおよびレコードとデータのタイプの基礎的な情報を確認しておくことをお勧めします。
リソース
関連ビデオ
WWDC23
WWDC21
-
ダウンロード
Simon Manningです CloudKitチームで エンジニアをしています これから CloudKitを使って Appのユーザー間のデータ共有を サポートする方法について紹介します まず GitHubで公開されている サンプルのSharing Appを 紹介します 次に Appで共有を作成したり その共有に他のユーザーを 招待したりできるようにする方法を 説明します また Appで 参加ユーザーの代わりに 共有したり 招待を受けたりする方法 共有レコードを取得する方法 カスタマイズされた共有体験を Appで提供する方法 そして最後にCloudKitで利用できる ゾーンベースの共有モデルについて 説明します まずは サンプルのSharing Appです CloudKitは AppがiCloud上のデータベースに アクセスするための フレームワークです これは 複数のCKDatabaseが格納された CKContainerにアクセスできる APIです 各コンテナには すべてのユーザーが レコードを読み書きできる パブリックデータベースが 1つ存在します デバイスがiCloudアカウントに ログインしている場合 Appはそのユーザーの データが含まれる プライベートデータベースにも アクセスできます また Appが共有を サポートしている場合 現在のiCloudユーザーと 共有されているデータは 共有CKDatabaseで Appも利用することができます ここでは レコードの所有者である ユーザーが共有を開始する方法と 参加者として 共有への招待を受ける方法の 両方を説明するために AppleのGitHubアカウントで 公開されている サンプルAppを参考にします 連絡先Appは 共有の最小限の例です 連絡先Appでは ユーザーのプライベートデータベースに 新しい連絡先レコードを作成すること 個々の連絡先を他のユーザーの Appと共有すること 他のユーザーから現在のユーザーに共有された すべての連絡先を表示することが サポートされています Sharingリポジトリには Swiftの並行処理を使った実装と 完了ハンドラを使った実装があり それぞれ別個のブランチで提供されています 共有を開始するには まず新しい連絡先レコードを作成し 所有者のプライベートデータベースに 保存します addContact関数は Sharingの Add New Contact UIから 呼び出されます これにより recordTypeが ContactであるCKRecordが 新規作成され 名前と電話番号が保存されます その後 そのレコードは プライベートデータベースに保存されます 他のユーザーのSharing Appと 連絡先レコードを共有するには まず共有を作成します CloudKitでの共有は CKShareと呼ばれる特別な種類のレコードに 依存しています ユーザーがレコードを 他の人と共有する場合は ユーザーに代わってCKShareを作成し それをプライベートデータベースに 保存します このユーザーは 共有するすべてのレコードを 所有しているため 共有レコード自体の 所有者でもあります この共有レコードは 共有されるデータ 共有している相手 共有参加者に与えられているアクセス権 を定義する 信頼できる情報源となります 共有アクセス権とは 共有の所有者が 共有レコードへのアクセスを コントロールする方法です publicPermissionプロパティは その共有へのリンクを持つユーザーの アクセス権レベルを定義します デフォルトでは URLだけを持つユーザーは アクセスできません このプロパティのアクセス権の 範囲を拡大すると 共有のURLを持つすべてのユーザーが 参加できるようになります また 招待された各参加者の アクセス権レベルは 共有の所有者によって決定されます 参加者オブジェクトは ユーザーの識別情報や 招待の受け入れ状況などを含む 特定のユーザーの 共有への参加を 定義したものです アクセス権は参加者オブジェクトごとに 個別に設定され 各参加者はCKShareの参加者配列に 追加されます ユーザーのプライベートデータベースから レコードの共有を開始するには まず共有のルートレコードを決定します この共有には ルートレコードと その子レコードが すべて含まれます その後 CKShareレコードを作成し ルートレコードとCKShareの両方を 現在のユーザーの プライベートデータベースに保存します Sharing Appでは createShare関数により 共有する連絡先レコードで CKShareが初期化されます CKShareとルートレコードが 所有者のプライベートデータベースに 保存され CKShareは招待プロセスを 開始するUIに 戻されます CKShareとルートレコードの両方を 同じ操作で一緒に保存する 必要があります CKShareだけを保存すると CloudKitにルートレコードが すでに存在していても エラーになります ルートレコードを共有すると その子レコードも共有されます ここまでで Appで共有を作成する方法を 説明しました これから 共有に参加するためのユーザー 招待の設定および送信方法について 説明します サンプルAppではUICloudSharing ControllerというUIKitが提供する ビューコントローラを使用しています このコントローラをAppに 実装することで ユーザーは簡単にCKShareに 参加者を招待したり アクセス権を設定したり 参加者を閲覧および管理したり レコードの共有を中止したり することができます これは 新しい連絡先レコードを 共有する際に UICloudSharing Controllerが サンプルのSharing Appに 表示される様子です ユーザーは共有招待の 送信方法を選択でき アクセス権は 「Share Options」セクションで 設定できます コントローラのデリゲート プロトコルを実装して 共有に関する追加情報を提供したり 共有イベントやエラーに関する通知を 受け取ったりできます ここで紹介した方法以外にも 共有招待に表示する アイテムタイトル アイテムタイプ サムネイル画像を 設定する方法があります 共有の所有者が招待を送信したら Appは招待された参加者に代わって その共有を処理し 受け入れる必要があります AppのInfo.plistで CKSharingSupportedブール値を 有効にします これにより Appが共有をサポート していることをシステムに知らせます そして 招待URLを処理して 関連するデリゲート関数を 呼び出す必要があります 共有招待が開かれ 受け入れられると Appはデリゲートメソッドの コールバックを通じて CKShare.Metadataオブジェクトを 受け取ります Appはこのメタデータを使用して 現在のユーザーが この共有を受け入れて 参加していることを コンテナのaccept関数または CKAcceptSharesOperation を通じて CloudKitに通知します applicationDelegateの ライフサイクルでは システムがAppを起動し userDidAccept CloudKitShareWith: shareMetadataの デリゲートメソッドを呼び出します shareMetadataオブジェクトで 提供された識別子を使って CKContainerオブジェクトを 初期化します 次に CKContainerで acceptを呼び出し shareMetadataオブジェクトを渡して 現在のユーザーが共有を受け入れたことを CloudKitに知らせます ここまでで プライベートレコードを 共有するための 招待の送信と その招待の処理と受け入れについて 説明しました 次に ユーザーが参加している共有レコードを Appで取得および変更する方法を 説明します 現在のiCloudユーザーと 共有されているレコードは Appコンテナの sharedCloudDatabase内で 利用できます 共有データベースでは 同じCloudKit APIを使用して プライベートデータベースや パブリックデータベース内のデータの 取得 クエリ実行 操作を することができます サンプルAppでは 共有レコードは fetchSharedContacts関数で 取得されています recordZoneChanges操作 を使用して 変更が徐々に取得および処理されます この操作が完了するたびに 取得すべき変更点が まだあるかどうかを表す moreComingブール値の プロパティが返されます また ゾーンの履歴における 特定の変更を指す changeTokenも返されます このchangeTokenを使うと その時点以降に発生した変更のみを 取得することができます 最初に取得する場合や ゾーンの履歴にあるすべての変更を 再取得する場合は changeTokenをnilに設定します アクティブな共有の管理は 現在のユーザーによって異なります 共有の所有者の場合は 所有者のプライベートデータベースから CKShareレコードを削除することで 共有を中止することができます 所有者は 共有ルートレコードを 完全に削除することもできます CKShareレコードオブジェクトの removeParticipant関数では 参加者を個々に 削除することができます これはParticipantプロパティで 確認できます そして最後に UICloudSharing Controller UIでは 所有者がすべての参加者との共有を 中止することができます 共有に参加しているユーザーの場合 ユーザーの共有データベースから ルートCKRecordを削除するか UICloudSharing Controller UIを 使用することで 参加を中止することができます なお ユーザーの 共有データベースから ルートCKRecordを削除しても 参加者の共有への参加が 中止されるだけで 元のルートレコードは 所有者のプライベートデータベースに そのまま残ります サンプルのSharing Appでは UICloudSharing Controllerを使用して 参加者の検索と招待を 処理していますが この機能をCloudKit APIで 実装することにより 共有の所有者と参加者のユーザー体験を カスタマイズすることもできます まず CKShareに招待する参加者を 検索するには 該当するCKContainerの CKFetchShare ParticipantsOperationや shareParticipants関数 を使います パラメータとして 電話番号 Eメール ユーザーレコードIDを使用できます 次に この参加者に 特定のアクセス権レベルを設定し addParticipant関数を使用して 参加者をCKShareに追加します 変更したCKShareは 必ずプライベートデータベースに 戻して保存してください 保存されたCKShareレコードには urlプロパティがあります このプロパティを 招待する参加者に送信し その参加者が処理および受け入れを行います 招待URLを使用して 共有の CKShare.Metadataを取得し Appのコンテナの accept(shareMetadata) 関数で 参加を確認できます Sharing Appにおける CloudKitの共有では ルートレコードとして 1つのレコードが共有され その子レコードも 共有されるという 階層型の共有モデルが 使用されています ゾーン共有は iOS 15で導入された新しいモデルで レコードのゾーン全体を 共有することができます ゾーンを共有する場合 1つのレコードの階層だけでなく ゾーン内のすべてのレコードが共有されます ゾーン共有は レコードゾーン内の すべてのレコードに影響を与えるため このタイプの共有を 同じレコードゾーン内の階層型共有と 共存させることはできません つまり 1つのゾーン内に 1つ以上の階層型共有を設置するか 1つのゾーン全体を共有するかのどちらかです ゾーン共有と階層型共有には それぞれメリットがあり ユースケースに応じて 使い分ける必要があります サンプルのSharing Appの場合 連絡先のグループが 別々のレコードゾーンで 構成されていれば ゾーン共有が便利です ゾーン共有を使えば それらの連絡先グループを 他のユーザーと 共有することができます ゾーン共有について詳しくは WWDC21の 「CloudKitの新機能」をご覧ください ゾーン全体の共有を新しく作成するには 共有するカスタムレコードゾーンのIDで 初期化し プライベートデータベースに保存します 共有の招待を受け入れて 共有データベースから レコードを取得するプロセスは 以前と同じです ある共有がゾーン全体の共有であるかどうかは そのrecordIDの recordNameプロパティを確認します 値がCKRecordNameZone WideShareの場合は 共有レコードゾーンが 管理されています そうでない場合は 共有レコード階層が 管理されていることになります このセッションでは Appで共有を サポートする方法を説明しました CKShareレコードを作成して ユーザー同士のコラボレーションを サポートしましょう UICloudSharing Controllerを使用して 共有UIを作成したり ニーズに合わせて実装を カスタマイズしたりできます 従来のFetch APIや Modify APIを使用して 共有クラウドデータベースから 共有レコードにアクセスできます そして ゾーン共有を導入することで メリットが得られる Appの共有の新しいユースケースを 探ってみましょう ご視聴ありがとうございました
-
-
1:58 - Create a new Contact record
// Create a new Contact record func addContact(name: String, phoneNumber: String) async throws { let id = CKRecord.ID(zoneID: recordZone.zoneID) let contactRecord = CKRecord(recordType: "Contact", recordID: id) contactRecord["name"] = name contactRecord["phoneNumber"] = phoneNumber try await privateCloudDatabase.save(contactRecord) }
-
4:09 - Preparing a CKShare
// Preparing a CKShare func createShare(contactRecord: CKRecord) async throws -> CKShare { let share = CKShare(rootRecord: contactRecord) try await privateCloudDatabase.modifyRecords( saving: [contactRecord, share], deleting: [] ) return share }
-
5:25 - UICloudSharingControllerDelegate
// UICloudSharingControllerDelegate public protocol UICloudSharingControllerDelegate { // ... // Called after the CloudKit sharing controller failed to save the share record. func cloudSharingController(UICloudSharingController, failedToSaveShareWithError: Error) // Called after the CloudKit sharing controller saves the share record. func cloudSharingControllerDidSaveShare(UICloudSharingController) // Called after the user decided to stop sharing the record. func cloudSharingControllerDidStopSharing(UICloudSharingController) }
-
6:27 - Processing an accepted share invitation
// Processing a user’s acceptance of a share invitation func application( _ application: UIApplication, userDidAcceptCloudKitShareWith shareMetadata: CKShare.Metadata ) { let container = CKContainer(identifier: shareMetadata.containerIdentifier) Task { do { try await container.accept(shareMetadata) } catch { // Handle errors that may occur } } }
-
7:24 - Fetching shared records
// Fetching records shared with the current iCloud user func fetchSharedContacts(in zone: CKRecordZone) async throws { var changeToken: CKServerChangeToken? = nil var moreChangesComing = true while moreChangesComing { let changes = try await sharedCloudDatabase.recordZoneChanges( inZoneWith: zone.zoneID, since: changeToken ) // Process changes as needed (modifications and deletions) processChanges(changes) moreChangesComing = changes.moreComing changeToken = changes.changeToken } }
-
9:16 - Search: Phone Number
// Search by phone number let phoneNumber = "417-555-9311" let participant = try await container.shareParticipant(forPhoneNumber: phoneNumber)
-
9:16 - Search: Email Address
// Search by email address let emailAddress = "dave_knox@icloud.com" let participant = try await container.shareParticipant(forEmailAddress: emailAddress)
-
9:16 - Search: Record ID
// Search by user record ID let participant = try await container.shareParticipant(forUserRecordID: recordID)
-
9:32 - Add participant to a share
// Add participant to existing CKShare record func addParticipant(_ participant: CKShare.Participant, to share: CKShare) async throws { participant.permission = .readWrite share.addParticipant(participant) try await privateCloudDatabase.save(share) }
-
9:47 - Confirm invitation acceptance
// Fetch CKShare.Metadata and confirm accepting share from a given URL func confirmShareParticipation(from url: URL) async throws { let shareMetadata = try await container.shareMetadata(for: url) try await container.accept(shareMetadata) }
-
11:14 - Share an entire record zone
// Create a CKShare sharing an entire record zone func createAndSaveShare(for zone: CKRecordZone) async throws -> CKShare { let share = CKShare(recordZoneID: zone.zoneID) try await privateCloudDatabase.save(share) if share.recordID.recordName == CKRecordNameZoneWideShare { // This is managing a shared record zone } return share }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。