ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
カスタムコラボレーションAppを メッセージAppと統合する
SharedWithYouフレームワークが、Appの連携基盤を強化する仕組みをご覧ください。連携コンテンツのセキュアな招待状を送信し、参加者の変更を同期する方法を紹介します。また連携するメッセージ内で、コンテンツの更新情報を表示する方法についても解説します。 SharedWithYouの導入については、WWDC22の「Shared with YouをAppに追加する」をご覧ください。共同制作のUI APIに関する概要については、WWDC22の「メッセージAppで共同制作の体験を強化する」をご覧ください。 (注:APIは、今後のベータ版で利用可能になります。)
リソース
関連ビデオ
WWDC22
-
ダウンロード
♪♪ メッセージ App team の Devin Clary です メッセージ App engineer の Lance です カスタム共同制作 App を メッセージ App と統合するお話しです コラボレーション = 共同制作は チャットで始まります iOS16 と macOS Ventura では カスタムの共同制作体験を チャットの中にそのまま持ち込めます 共同制作のライフサイクルを説明します 次に App の共同制作コンテンツを メッセージ App で共有できるように 準備する方法を紹介します 続いてプライバシーを犠牲にすることなく 受信者のアクセス権を確認し 参加者の変更に対応するために必要な すべてのことを説明します 最後にメッセージ App のチャットに App からコンテンツに関する通知を ポストする方法です ここでは App に 既存の共同制作インフラを持ち ユニバーサルリンクをすでに 採用していることを想定しています 「AppにShared with Youを追加する」や 「メッセージAppで 共同制作体験を強化する」 で紹介したいくつかの概念を基に説明します まず最初にカスタム共同制作メッセージの ライフサイクルを説明し この API によってユーザーがこれまでよりも 迅速に共同制作を開始できるように なることを説明します ユーザーが App からメッセージを通じて 共同制作を共有することを決定すると まずコンテンツを表すメタデータを作成します このメタデータには ユーザーがメッセージを 送信する前に設定できる共有オプションや カスタマイズ可能なその他の プロパティが含まれています 次にそのメタデータを共有シートに提供するか ドラッグ&ドロップします これによりコンテンツのドラフトが メッセージの作成フィールドに ステージングされます 共同制作はユニバーサルリンクで 表現する必要があります リンクはすぐにでも作成できますが メッセージを送信する直前まで 延期したほうがよいでしょう これはメッセージ App のリンク作成が メッセージ作成フィールドで設定される 共有オプションや受信者に 依存する場合に有効です 共有オプションと受信者を選択し 送信ボタンをタップします メッセージ App は メッセージが送信される前に ユニバーサルリンクと デバイスに依存しないコンテンツの識別子を App に要求します メッセージ App はその識別子を用いて 特定の共同制作メッセージの受信者を表す 暗号化された ID を提供します この ID を使用すれば 受信者はどのデバイスでも 共同制作のリンクを開くことができます App はこれらの ID をサーバーに保存し
この手順が完了すると メッセージが受信者に送信されます 受信側のデバイスで何が起こるかを説明します 目的はアクセスを確認することであり 受信者の ID とサーバー上のアカウントを ペアリングします 受信者がリンクを開くと 他のリンクの場合と同様に あなたの App が URLを開くために 呼び出されます 要求したユーザーアカウントの そのドキュメントへのアクセスが初めての場合 受信者のデバイスによって暗号署名された ユーザー ID の認証を システムに問い合わせます App はその署名付き ID 証明を あなたのサーバーに送信し認証を受けます 署名が有効な場合サーバーは 送信デバイスから以前に提供された ID と 証明書を比較します 一致した場合サーバーは そのユーザーからのアクセスを許可します これでメッセージ受信者は アカウント情報を交換することなく コンテンツに安全かつ迅速に アクセスできるようになりました これが共同制作メッセージの ライフサイクルです! 共同制作を開始するための APIについて詳しく見てみましょう 共同制作にはメタデータが必要です SharedWithYou フレームワークの新しいクラス SWCollaborationMetadata を使用します このクラスには共同制作を構成するための 以下のプロパティがあります 共有前にコンテンツを参照するための ローカル識別子であるコンテンツのタイトル 共有元のアカウントについて ユーザーに情報を提供する 開始者名とアカウントハンドル デフォルトの共有オプションなどです メタデータオブジェクトの作成と プロパティの設定方法を説明します SWLocalCollaborationIdentifier を 文字列で初期化し ローカル識別子を作成します この文字列は App がローカルで コンテンツを識別できれば良く デバイスをまたいで識別する必要はありません ローカル識別子で 新しいメタデータインスタンスを初期化します Foundation フレームワークの PersonNameComponents を使用して コンテンツのタイトルイニシエーターの アカウントハンドルと名前を設定します ハンドルと名前は ユーザーが共有元のアカウントを 確認できるように ローカルにのみ表示されます 次に defaultShareOptions の設定ですが その前に オプションの仕組みについて説明します 共有オプションはユーザーが メッセージ App や共有シートで設定します ユーザーが選択したオプションは メッセージが送信される前に提供されます 共有オプションに含まれるのは 誰がコンテンツにアクセスできるのか 誰が共同制作に編集を加えることができるか といった項目です オプションの設定では SWCollaborationOption などのクラスを使います グループ化の仕方によって オプションは個々のスイッチや 設定に対する相互排他な値で表されます オプションはタイトルと識別子を持ち 選択されるか選択されないかのどちらかです オプションのグループを表現するために 2つのクラスがあります SWCollaborationOptionsGroup と SWCollaborationOptions PickerGroup スイッチのコレクションには SWCollaborationOptionsGroup を使い SWCollaborationOptions PickerGroup は 設定に対して相互に排他な値を表します SWCollaborationShareOptions は メタデータの defaultShareOptions プロパティに 設定される オプショングループの完全なセットを定義します またオプションを説明するための 要約文字列を提供することもできます オプションクラスの説明に続き 次はその使い方の例です このコードでは 2 つのオプショングループを 定義しています 最初のグループは 識別子と2 つのオプションで初期化されます 識別子はユーザー選択したオプションを 後で識別するために使う任意の文字列です これはピッカーグループなので 選択肢は相互に排他です このグループはコンテンツの権限設定 readwrite または readonly を表します 最初のオプションがデフォルトで選択されます タイトルには このグループを説明する文字列が設定されます 2番目のオプショングループも 同様に初期化され 2つのオプションが含まれます これらは通常のオプションで ユーザーの投稿やコメントを 個別に許可するかどうかを設定します 最後に 2つのオプショングループを利用して SWCollaborationShareOptions の インスタンスを初期化し メタデータに設定します コンテンツの共有の決定方法に応じて メタデータを共有シートまたは ドラッグ&ドロップに提供します SwiftUI であれば SWCollaborationMetadata は 新らしい ShareLink API と 互換性があります 詳細は「Transferableの紹介」と 「SwiftUI の新機能」をご覧ください SwiftUI のプロキシ表現を使った 共同制作のサポートがいかに簡単か紹介します Transferable モデルオブジェクトの ProxyRepresentation で 共同制作メタデータのインスタンスを返します そのビューから そのモデルオブジェクトで ShareLink を初期化します UIKit や AppKit では NSItemProvider で 共有をサポートします SWCollaborationMetadata は NSItemProviderReading / Writing に準拠しており メタデータのインスタンスを登録するだけで 共同制作のサポートが可能です できるだけ多くのチャネルで 共有をサポートするために コンテンツの複数の表現を登録します 例えばメッセージ App では ファイル表現をサポートすると 「コピーを送信」が自動的に提供されます 使用するのはUIActivityViewController付き NSItemProvider API を iOS と iPadOS では UIDragItem で macOS では NSSharingServicePicker で使用します これは iOS の共有シートの設定です NSItemProvider のインスタンスを作成し 前の例で作成した共同制作メタデータを 全プロセスに可視と設定した上で 登録します UIActivityItemsConfiguration を アイテムプロバイダで初期化し UIActivityViewController を そのコンフィグで初期化します 最後にビューコントローラーを表示します ドラッグ&ドロップに対応させるのも簡単です NSItemProvider を初期化して 同じようにメタデータを登録し アイテムプロバイダで UIDragItem を作成して ドラッグ&ドロップ API で使用します API は macOSでも同様で 共有ポップオーバー用です 再びアイテムプロバイダを設定し 今回はこれを用いて NSSharingServicePicker を初期化します ターゲットビューを基準にして ピッカーを表示します macOS でのドラッグ&ドロップは NSItemProvider ではなく NSPasteboardItem を利用します これをサポートするために SharedWithYou は NSPasteboardItem extension を エクスポートします ドラッグ&ドロップのサポートのために その extension を利用して 新しい NSPasteboardItem のインスタンスに コラボレーションメタデータを設定します これだけで共同制作コンテンツの下書きが メッセージ App で表示されます 次にユーザーが送信ボタンをタップすると システムはあなたの App と連携して 共有を設定します これを行うのは SWCollaborationCoordinator で これはシングルトンであり グローバル共有のインスタンスを意味します 共有インスタンスはデリゲートの actionHandler で共同制作を調整します App は共同制作を調整できるよう 必要なときに バックグラウンドで起動されます そのため 起動後すぐにデリゲートを登録し タイムアウトを回避するために即座に アクションを処理する必要があります ここでは App の起動終了後に 共同制作コーディネーターを セットアップする方法を説明します 共有プロパティからシングルトンの コーディネーターのインスタンスに アクセスします App delegate の didFinishLaunchingWithOptions で actionHandler を SWCollaborationActionHandler の プロトコルに準拠したオブジェクトに 設定します アクションハンドラーには SWAction クラスを使用します SWAction は App が実行することを 期待されている作業を表します アクションが終了したら完了をマークし それ以外は失敗となります App の最初のアクションは 共同制作開始のアクションです SWStartCollaborationAction は 先に設定した共同制作メタデータを含んでいて ユーザーが選択した 共有オプションで更新されます 必要な設定を行ったら ユニバーサルリンクと デバイスに依存しない共同制作の ID を使って 開始アクションを完了します 明示的に開始アクションを失敗させた場合 メッセージはキャンセルされます ここでは サーバーリクエストの例を用いて 開始アクションを処理する実装を示します まずアクションのメタデータプロパティから ローカル識別子と ユーザーが選択した共有オプションを取得し 識別子とオプションを使って 共同制作準備のサーバーリクエストを作り サーバーにリクエストを送信します ここでは非同期の await を使用しています 最後にユニバーサルリンクと デバイスに依存しない識別子を レスポンスから取得して アクションを実行します エラーが発生した場合はアクションを失敗させ メッセージをキャンセルします 開始アクションが成功した場合 システムは共同制作の参加者を更新する 2番目のアクションを App に送信します SWUpdateCollaborationParticipantsAction には 参加者の暗号化された ID が 含まれています この ID は前の開始アクションで作られた 共同制作識別子から作られます これらの識別子はコンテンツに紐づけて サーバーに保存します このデータは受信者のデバイスで アクセスの確認に使用されます 最後にこのアクションを実行すると メッセージ App で ユニバーサルリンクが送信されます この例では参加者の更新アクションを 処理する方法を示しています アクションのメタデータから 共同制作識別子を取得します 開始アクションを処理する際に 作成された識別子です 次に addIdentities プロパティを使用して サーバーに保存する参加者データを取得します 各 ID は root hash という Data プロパティを持ちます これは後で使用するために サーバーに保存しておくべきデータです 詳細は後ほど アクセスの検証セクションで Lance が説明します 別のサーバーリクエストを設定します 今回はターゲット識別子を持つ 共同制作に参加者を追加します 前と同じようにサーバーにリクエストを送信し アクションを実行または失敗させます 今回 fulfill メソッドは パラメーターを受け取りません これで共同のセットアップが完了し App はメッセージの受信者からの アクセス許可に必要なものが揃いました Lance がその方法をお見せします どうも Devin このセクションでは 前のステップでサーバーに保存した ID データを使って 受信者にアクセスを提供する方法を紹介します この検証には SWPersonIdentity の rootHash プロパティを使用します デバイス上で参加者を 一意に識別するために用意された値です 検証を実行するためには ルートハッシュの計算方法の理解が必要です 紹介しましょう 共同制作のメッセージが送信されるとき 実際には各人のデバイスに個別に送信されます メッセージ App は暗号の公開鍵を使って 各デバイスを識別します これらのデバイスにのみ アクセス許可することが目的であるため ルートハッシュは各受信者に登録された 公開鍵から導出します ルートハッシュはマークルツリーと呼ばれる データ構造のルートノードです マークルツリーは一連の ハッシュ操作で構築される二分木です 公開鍵に基づきユーザーの ID を導き出すために 鍵は このツリーのリーフとして使用されます マークルツリーで使用される ハッシュアルゴリズムは ルートノードがその鍵のセットからしか 計算できないことを保証します この例では このユーザーは3つの機器と 3つの公開鍵を持っています 鍵は鍵の多様化と呼ばれる プロセスで App の共同制作識別子ごとに一意となります 登録された機器数の追跡を防ぐため セットにはランダムの鍵が詰め込まれます ツリーのリーフノードは 多様化された鍵の パディングセットをハッシュ化することで 作成されます このツリーのハッシュ処理には SHA256 アルゴリズムが使用されます 次にリーフノードの各ペアを連結し その親ノードを導き出すために ハッシュ化します この処理を親ノードで繰り返し 1つのルートノードが残るまで繰り返します これが 受信者のデバイス間で受信者の ID を 一意に表現するために使用される ルートハッシュです 完全なマークルツリーから ノードのサブセットを使用して ルートハッシュを生成することが 可能であることに注意してください このツリーのルートハッシュは ハッシュ H4 7 11 と 多様化した公開鍵 P3 だけを使って 再現することができます まず公開鍵をハッシュし 欠落リーフノード H3 を取得し H3 と H4 を使用して H8 を生成します そして指定された H7 H8 ノードで H10 を生成し 最後に H10 と H11 で ルートハッシュを生成します ここで重要なのは ツリー全体の再構築をしなくても 与えられたルートハッシュを生成するために 公開鍵 P3 が使われたことを 証明できることです これを行うために必要なノードのサブセットを 包含の証明と呼びます App でユニバーサルリンクが開かれると 検証が開始されます これを行うには リンクの連携の確認が必要です SWCollaborationHighlight は連携リンクで SWHighlightCenter から取得されます 共同制作のハイライトを使用して 包含の証明を生成します 包含証明を表現するには SWPersonIdentityProof クラスを使用します 検証を実行するには オブジェクトを暗号署名で生成し サーバーに送信します getSignedIdentityProof メソッドで 証明を取得します これは SWCollaborationHighlight と デバイスで署名されたデータです 署名を使用することで 悪意ある者からの要求を 拒否することができます データはサーバーが要求したチャレンジか デバイスで生成されたナンスです この例ではチャレンジ方式を採用しています この URL は App の UIApplicationDelegate のメソッドに渡されます この URL は共同制作を表す ユニバーサルリンクで SWHighlightCenter から該当の SWCollaborationHighlight を取得します 次にサーバーにチャレンジを要求し 戻ってきたデータを getSignedIdentityProof メソッドに ハイライトと一緒に渡すと 署名付きの ID 証明が得られます このデータを検証するためにサーバーが 何をすべきかについては 後ほど説明します これで署名した証明書を サーバーに送って確認できます 最後にユーザーインターフェイスを その結果で更新します App はサーバーに 公開鍵と署名データと共に証明書を送信します データへの署名は P-256 楕円曲線の デジタル署名アルゴリズムで行われ ハッシュ関数として SHA256 を使用します ID 証明の公開鍵で データへの署名を検証します これは一般的に使用される 暗号化ライブラリで実行可能です 署名を検証すれば その ID 証明が公開鍵のデバイスから 送信されたと信頼できます 次に ID 証明を使用して ルートハッシュを再計算します こちらは前に見たツリーのサンプルで SWPersonIdentityProof に含まれる例です これでルートハッシュを再構築します 公開鍵は P3 です 包含ハッシュは H4 7 11 です ローカルキーのインデックス 2 が ツリー内の公開鍵の位置を示しています 証明のプロパティからルートハッシュを 再構築する実装の例を次に示します 再帰アルゴリズムが ツリーデータ構造でうまく機能しています 初回起動時に公開鍵のハッシュ 包含ハッシュのセット および公開鍵のインデックスを渡します 次に最初の包含ハッシュが導出され 公開鍵のインデックスは その鍵が兄弟鍵の 左側にあるか右側にあるかを確認するために チェックされます 選択されたハッシュが 正しい順序で連結されハッシュ化されます inclusionHashes 配列で 使用済みノードが削除され残りのノードが 同関数の再帰呼び出しに渡されます 公開鍵のインデックスも更新され ツリーの次のノードに対応可能になります このシンプルな関数を使用して ID の証明があればルートハッシュを すばやく計算することができます サーバーは生成されたルートハッシュが 文書の所有者が持つルートハッシュの リストに含まれるか確認できます このハッシュは既知のハッシュのリストに 含まれているので サーバーはドキュメントへのアクセスを 許可することができます これで文書へのアクセスを許可できます! 本人確認の手順は次のとおりです まずユニバーサルリンクを処理しながら そのコンテンツの共同制作ハイライトを検索し 次にデータに署名し包含の証明を取得します 署名されたデータと証明書をサーバに送信し データの署名を確認します 包含証明を使用しルートハッシュを生成し 最後にルートハッシュを そのコンテンツに関連する 既知の ID のリストと比較します これでアクセスの確認方法は 理解できたと思います 次はメッセージ App での 参加者の変更について説明します メッセージ App で共同作業をしている グループチャットの参加者が変更されると ユーザーはメッセージ App の スレッドのバナーで その変更を App に反映させることができます このシナリオでは App は追加削除された ID を含む別の SWUpdateCollaboration ParticipantsAction を受信します 共同制作設定時の処理と 同じコードを使いますが 参加者の削除処理も必要です 削除する場合は その ID を検索し アクセス権を取り消すだけです アカウントがまだ関連付けられていない場合は 単純にデータベースから ルートハッシュを削除してください Devin が説明した 参加者の更新動作の実装を説明しましょう この例では 削除された ID のプロパティを使い それらを同様の削除要求 API に渡します このコードでは削除された ID の処理のみですが 完全な実装では 追加と削除された ID の処理が必要です これで参加者の変更に対応できます! 最後に共同制作に変更があると Appはその変更を通知し メッセージ App に表示します サポートされる通知にはいくつかあります ここで説明します 通知はリンクが 共有されたチャットにバナーとして表示され バナーには変更内容と 変更者の説明が表示されます このチャットで Charlie は パンのレシピに編集を加えました 表示ボタンをタップすると すぐに該当のコンテンツに戻れます 通知を表すために SWHighlightEvent という プロトコルがあります これは SWHighlightCenter API から取得した SWHighlights で初期化されます メッセージ App は複数のイベントに対応しています コンテンツの更新やコメントに対する変更 参加者の参加退出時のメンバーシップイベント ユーザーが言及されたときの メンションイベント コンテンツの移動や削除に関する パーシスタンスイベント 以下は編集の変更イベントを 共同制作にポストする方法です highlight center API を使用して 対象識別子の collaboration highlight を取得します これは共同制作開始時に定義した識別子で コンテンツの変更時に App で用意する必要があります 次に変更イベントのインスタンスを作成し 初期化にはハイライトと トリガー列挙値を指定します この場合は edit とします 再度ハイライトセンターから そのイベントの通知をポストします 同様にメンバーの変更は メンバーシップイベントをポストします 今回は addedCollaborator や removedCollaborator を渡します ユーザーのメンションに対応していれば メンションイベントをポストできます 個人 ID を ルートハッシュで初期化します アクセス認証の際に App の個人アカウントと ID の関連付けを思い出して 同様にメンションイベントをポストし 言及された ID をパラメータとして渡します 通知は該当ユーザーへの メッセージにのみ表示されます 最後にコンテンツの移動と 名前変更または削除の場合です パーシスタンスイベントを使用します ここではトリガータイプを使用して ユーザーがコンテンツの 名前を変更したことを表示します App が共同作業者に通知することで その更新情報をメッセージ App で 直接受け取れます いくつかの手順に従い 共同制作の機能を メッセージ App と統合する準備ができました コンテンツの共同制作を設定し 参加者のアクセスを暗号化して検証し 参加者の変更を追跡し メッセージ App で通知をポストして ユーザーをコンテンツにつなげます 共同制作で使う UI 要素の詳細は “メッセージAppで共同制作の体験を強化する”を 参照してください あなたの App との共同制作を楽しみにしています! これで我々はサインオフします ありがとうございました ♪♪
-
-
4:21 - Configure SWCollaborationMetadata
let localIdentifier = SWLocalCollaborationIdentifier(rawValue: "identifier") let metadata = SWCollaborationMetadata(localIdentifier: localIdentifier) metadata.title = "Content Title" metadata.initiatorHandle = "user@example.com" let formatter = PersonNameComponentsFormatter() if let components = formatter.personNameComponents(from: "Devin") { metadata.initiatorNameComponents = components } metadata.defaultShareOptions = ...
-
6:34 - Configure SWCollaborationShareOptions
let permission = SWCollaborationOptionsPickerGroup(identifier: UUID().uuidString, options: [ SWCollaborationOption(title: "Can make changes", identifier: UUID().uuidString), SWCollaborationOption(title: "Read only", identifier: UUID().uuidString) ]) permission.options[0].isSelected = true permission.title = "Permission" let additionalOptions = SWCollaborationOptionsGroup(identifier: UUID().uuidString, options: [ SWCollaborationOption(title: "Allow mentions", identifier: UUID().uuidString), SWCollaborationOption(title: "Allow comments", identifier: UUID().uuidString) ]) additionalOptions.title = "Additional Settings" let optionsGroups = [permission, additionalOptions] metadata.defaultShareOptions = SWCollaborationShareOptions(optionsGroups: optionsGroups)
-
7:58 - SWCollaborationMetadata SwiftUI TransferRepresentation
struct CustomCollaboration: Transferable { var name: String static var transferRepresentation: some TransferRepresentation { ProxyRepresentation { customCollaboration in SWCollaborationMetadata( localIdentifier: .init(rawValue: "com.example.customcollaboration"), title: customCollaboration.name, defaultShareOptions: nil, initiatorHandle: "johnappleseed@apple.com", initiatorNameComponents: nil ) } } }
-
8:16 - Using a collaboration metadata TransferRepresentation with ShareLink
struct ContentView: View { var body: some View { ShareLink(item: CustomCollaboration(name: "Example"), preview: .init("Example")) } }
-
9:08 - iOS Share Sheet
func presentActivityViewController(metadata: SWCollaborationMetadata) { let itemProvider = NSItemProvider() itemProvider.registerObject(metadata, visibility: .all) let activityConfig = UIActivityItemsConfiguration(itemProviders: [itemProvider]) let shareSheet = UIActivityViewController(activityItemsConfiguration: activityConfig) present(shareSheet, animated: true) }
-
9:42 - iOS Drag and Drop
func createDragItem(metadata: SWCollaborationMetadata) -> UIDragItem { let itemProvider = NSItemProvider() itemProvider.registerObject(metadata, visibility: .all) return UIDragItem(itemProvider: itemProvider) }
-
9:58 - macOS Sharing Popover
func showSharingServicePicker(view: NSView, metadata: SWCollaborationMetadata) { let itemProvider = NSItemProvider() itemProvider.registerObject(metadata, visibility: .all) let picker = NSSharingServicePicker(items: [itemProvider]) picker.show(relativeTo: view.bounds, of: view, preferredEdge: .minY) }
-
10:18 - macOS Drag and Drop NSPasteboardItem extension
func createPasteboardItem(metadata: SWCollaborationMetadata) -> NSPasteboardItem { let pasteboardItem = NSPasteboardItem() pasteboardItem.collaborationMetadata = metadata return pasteboardItem }
-
11:22 - Set up SWCollaborationCoordinator
private let collaborationCoordinator = SWCollaborationCoordinator.shared func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool { // Conform to the SWCollaborationActionHandler protocol collaborationCoordinator.actionHandler = self }
-
12:27 - SWStartCollaborationAction
func collaborationCoordinator(_ coordinator: SWCollaborationCoordinator, handle action: SWStartCollaborationAction) { let localID = action.collaborationMetadata.localIdentifier.rawValue let selectedOptions = action.collaborationMetadata.userSelectedShareOptions let prepareRequest = APIRequest.PrepareCollaboration(localID: localID, selectedOptions) Task { do { let response = try await apiController.send(request: prepareRequest) let identifier = response.deviceIndependentIdentifier action.fulfill(using: response.url, collaborationIdentifier: identifier) } catch { Log.error("Caught error while preparing the collaboration: \(error)") action.fail() // cancels the message } } }
-
13:40 - SWUpdateCollaborationParticipantsAction
func collaborationCoordinator(_ coordinator: SWCollaborationCoordinator, handle action: SWUpdateCollaborationParticipantsAction) { let identifier = action.collaborationMetadata.collaborationIdentifier let participants: [Data] = action.addedIdentities.compactMap { $0.rootHash } let addParticipants = APIRequest.AddParticipants(identifier: identifier, participants) Task { do { try await apiController.send(request: addParticipants) action.fulfill() // sends the URL provided by the start action } catch { Log.error("Caught error while adding participants to collaboration: \(error)") action.fail() // cancels the message } } }
-
19:12 - Retrieve a signed identity proof for a highlight
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let highlightCenter: SWHighlightCenter = self.highlightCenter let challengeRequest = APIRequest.GetChallengeData() Task { do { let highlight = try highlightCenter.collaborationHighlight(for: url) let challenge = try await apiController.send(request: challengeRequest) let proof = try await highlightCenter.getSignedIdentityProof(for: highlight, using: challenge.data) let proofOfInclusionRequest = APIRequest.SubmitProofOfInclusion(for: proof) let result = try await apiController.send(request: proofOfInclusionRequest) documentController.update(currentDocument, with: result) } catch { Log.error("Caught error while generating proof of inclusion: \(error)") } } }
-
21:20 - Example code for root hash generation
func generateRootHashFromArray(localHash: SHA256Digest, inclusionHashes: [SHA256Digest], publicKeyIndex: Int) -> SHA256Digest { guard let firstHash = inclusionHashes.first else { return localHash } // Check if the node is the left or the right child let isLeft = publicKeyIndex.isMultiple(of: 2) // Calculate the combined hash var rootHash: SHA256Digest if isLeft { rootHash = hash(concatenate([localHash, firstHash]), using: .sha256) } else { rootHash = hash(concatenate([firstHash, localHash]), using: .sha256) } // Recursively pass in elements and move up the Merkle tree let newInclusionHashes = inclusionHashes.dropFirst() rootHash = generateRootHashFromArray( localHash: rootHash, inclusionHashes: Array(newInclusionHashes), publicKeyIndex: (publicKeyIndex / 2) ) return rootHash }
-
24:12 - SWUpdateCollaborationParticipantsAction - removing participants
func collaborationCoordinator(_ coordinator: SWCollaborationCoordinator, handle action: SWUpdateCollaborationParticipantsAction) { // Example of removing participants only. Handle the added identities here too. let identifier = action.collaborationMetadata.collaborationIdentifier let removed: [Data] = action.removedIdentities.compactMap { $0.rootHash } let removeParticipants = APIRequest.RemoveParticipants(identifier: identifier, removed) Task { do { try await apiController.send(request: removeParticipants) action.fulfill() } catch { log.error("Caught error while adding participants to collaboration: \(error)") action.fail() } } }
-
25:54 - Post an SWHighlightChangeEvent Notice
func postContentEditEvent(identifier: SWCollaborationIdentifier) throws { let highlightCenter: SWHighlightCenter = self.highlightCenter let highlight = try highlightCenter.collaborationHighlight(forIdentifier: identifier) let editEvent = SWHighlightChangeEvent(highlight: highlight, trigger: .edit) highlightCenter.postNotice(for: editEvent) }
-
26:39 - Post an SWHighlightMembershipEvent Notice
func postContentEditEvent(identifier: SWCollaborationIdentifier) throws { let highlightCenter: SWHighlightCenter = self.highlightCenter let highlight = try highlightCenter.collaborationHighlight(forIdentifier: identifier) let editEvent = SWHighlightChangeEvent(highlight: highlight, trigger: .edit) highlightCenter.postNotice(for: editEvent) }
-
26:50 - Post an SWHighlightMentionEvent Notice
func postMentionEvent(identifier: SWCollaborationIdentifier, mentionedRootHash: Data) throws { let mentionedIdentity = SWPerson.Identity(rootHash: mentionedRootHash) let highlightCenter: SWHighlightCenter = self.highlightCenter let highlight = try highlightCenter.collaborationHighlight(forIdentifier: identifier) let mentionEvent = SWHighlightMentionEvent(highlight: highlight, mentionedPersonIdentity: mentionedIdentity) highlightCenter.postNotice(for: mentionEvent) }
-
27:23 - Post an SWHighlightPersistenceEvent Notice
func postContentRenamedEvent(identifier: SWCollaborationIdentifier) throws { let highlightCenter: SWHighlightCenter = self.highlightCenter let highlight = try highlightCenter.collaborationHighlight(forIdentifier: identifier) let renamedEvent = SWHighlightPersistenceEvent(highlight: highlight, trigger: .renamed) highlightCenter.postNotice(for: renamedEvent) }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。