ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Group Activitiesによるカスタムエクスペリエンスの構築
基本的なストリーミングとインタラクションを超えた、Group Activitiesフレームワークのパワーを存分に駆使した先進的なSharePlayエクスペリエンスを構築する方法について確認します。シンプルな描画Appをリアルタイムの共有キャンバスにする方法を紹介します。グループの参加者間でカスタムメッセージをやり取りするのに役立つGroupSessionMessengerなどのAPIを掘り下げ、カスタムのSharePlayエクスペリエンスで最後の仕上げをする方法を紹介します。
リソース
関連ビデオ
WWDC23
WWDC21
-
ダウンロード
(音楽) (Group Activities のユーザー体験の構築) こんにちは 私の名前はウィレムです Group Activities で働いているエンジニアです AngusとAdamが参加して Group Activities でユーザー体験を作成する 方法について話します まず このセッションで 開発中のアプリを紹介し なぜそれが Group Activities のサポートを追加するのに 最適な候補なのかを説明します 次に アクティビティの作成と セッション 管理の手順について説明し メディアエクスペリエンスを 作成する場合との違いについて 説明します 最後に ユーザーがアプリを 使用しているときの エクスペリエンスを向上させる 方法をいくつか紹介します Group Activities ではSharePlay を使用してデバイス間で共有 エクスペリエンスを構築できます メディア体験の創造に 焦点を当てているが だからといって創造性を放り出して アプリが複数の デバイスでどのように 体験できるかを見ることを 妨げるべきではない ここでは「Coordinate media experiences with Group Activities」 セッションで紹介したコンセプトを 基にしていきます それをチェックすることを 強くお勧めします
このセッションでは DrawTogether というアプリを使って FaceTimeで 一緒に絵を描けます 画面全体が キャンバスになっていて 誰もがランダムな色を 使って絵を描くことができます しかし シンプルであるにもかかわらず 友達と一緒に絵を描いたり その素晴らしい技術に感心したり 下手な絵を見て笑ったりするのは 信じられないほど楽しい 私自身も間違いなく 2番目のカテゴリーに入ります 私たちが目指すものの 簡単なデモをお見せしましょう
やあ みんな どうしたの? Adam質問があるのを見た うん Angusと私は話していましたが Angusはあなたは本当に 素晴らしい芸術家だと言って ピカソのクラスです 見せてくれ! Willemの絵を何枚か見たけど すごいわね Angus少し誇張してる だけだと思うでも 私にできることをお見せできて うれしいです DrawTogether アプリに行きましょう OK 牧歌的な風景ができますか? ええ...家を描くところから 始めましょうか OK Willemこの家は基本的に 見える日光浴の手伝いをしようかな 木に挑戦してみます あの木は高すぎます 太陽が低すぎる 草を足しましょう もっと木を描きましょう OK ええと 電話しようと思います 私たちは芸術家ではありません 今していることをやめるべきです そうしよう 後で話そういいか? OK バイ ご覧のように Group Activities API の中核的なエクスペリエンス の1つは 物理的に離れている間に一緒に何か をすることを可能にすることです リアルタイムのインタラクションを 解除することができ 他の人を見たり聞いたりする ことができるため ユーザーは即座に反応し 本当に魔法のような瞬間を 得ることができます これは アプリに Group Activities を統合する方法を検討する 際に留意すべきことです Group Activities を採用するには アクティビティーの作成と セッション管理の2段階があります これについては 「Coordinate media experiences」 セッションで詳しく説明しました このセッションでは アクティビティの作成から始めて ユーザー体験の構築時にこれらの 手順がどのように変化するかを 見ていきましょう Group Activityを作成するには 2つのパートがあります まずアクティビティを設定し 次にアクティビティを アクティブ化します メディアグループアクティビティ と比較すると カスタムアクティビティでは 設定パーツのみが異なります アクティビティを設定するときは すべての参加者間で共有する 具体的なエクスペリエンスについて 考える必要があります アクティビティには その経験を通じて一定のままである 全の情報を含める必要があります 「Coordinate media experiences with Group Activities」 セッションを見たことがある 人ならご存知でしょう GroupActivity プロトコルに準拠する DrawTogether 構造体を定義し タイトルに関連する メタデータを作成する メタデータ プロパティーを実装しました これをカスタムアクティビティ にするには メタデータに適切なタイプを 設定するだけです これを汎用に設定することで このアクティビティをカスタム アクティビティとして設定します メディアアクティビティと比較して カスタムアクティビティを 設定するにはこれだけで十分です それではXcodeに移り カスタムのグループ アクティビティーの 作成を開始しましょう まず最初に説明するコードについて 簡単に説明します DrawTogetherはSwiftUIアプリの ライフサイクルを使用するSwiftUIアプリです ContentViewは アプリのメインビューだ ビューの上部には 描画時に使用される色を 示すインジケータがあります その下にはCanvasView がある これはキャンバスを使用し キャンバス内のすべての ストロークを描画し ユーザ入力に基づいて キャンバスを更新します 最後に 下部には ControlBarがあり 描画中に便利なコントロールが いくつか含まれています 現在は キャンバスをクリアして 最初から作業を開始するための ボタンが1つあります キャンバス自体はストロークの 配列で構成され 各ストロークにはカラー 識別子 およびポイントの リストがあります キャンバスには ローカルユーザが現在描画している ストロークを表す activeStrokeと ユーザが使用する ストロークカラーもあります 次に アクティビティの設定から 始めましょう その前に Group Activities エンタイトルメントを追加する 必要があります これを行うには プロジェクト設定に移動し Signing&Capabilities タブで新しい機能を追加します グループアクティビティーを 検索して選択します これで資格が得られたので 最後にアクティビティーを 構成しましょう ファイル>新規>ファイル... Swiftファイルを選択し 新しいファイルを追加します それをDrawTogether と呼ぶ...
... 作成 をクリックします まず フレームワークを インポートします
次に GroupActivity プロトコルに準拠する DrawTogetherという 新しい構造体を定義します GroupActivity プロトコルには実装する必要が がある2つのプロパティがあります activityIdentifierとmetadata activityIdentifier については デフォルトの実装による ただしmetadata プロパティはまだ必要です これを追加しましょう
この計算されたプロパティで GroupActivityMetadata オブジェクトを作成し タイトルを設定します タイプも汎用に設定しました これは カスタムアクティビティに 不可欠です 最後に メタデータ オブジェクトを取得します
適切なタイミングで 有効にする必要があります 新しいボタンを追加します コントロールバーよりも 良い場所は? HStackの最初にボタンを 追加します
ボタンのラベルには SFシンボルを使用します アクションのクローズではカスタム Group Activity の新しいインスタンスを作成して activateを呼び出します 私たちの活動を活発にするために 必要なのはそれだけです ここでは カスタム Group Activity の構成方法とアクティブ化方法を 説明しました アクティビティ作成ステップには 次の2つの部分が必要です 次に セッション管理ステップ について説明する Angusにそれを引き継ぎます ありがとうWillem 次に Group Activities を使用してアプリケーションで カスタムデータを送受信する 方法について説明します これは グループアクティビティ を使ったユニークな SharePlay体験を生み出す ための中心的なものだ 「Coordinate media experiences with Group Activities」 というタイトルの前の セッションから セッションの受信 再生の準備 およびセッションへの参加の 3つのステップについて 理解している必要があります 再生同期の代わりに ユーザー体験のために セッションを設定する 必要があります その前に グループセッションを受信して参加 するためのコードを追加しましょう Xcodeに戻りまず ContentViewに移動して GroupActivities をインポートします
次に GroupSessionを 受信する非同期タスクを作成します
GroupSessionができたので それを保存する場所が必要です 「configureGroupSession」 と呼ばれる新しいメソッドを 使用してこれをCanvas オブジェクトに格納します
次に Canvasに移動し configureGroupSession メソッドを実装します
まず最初に GroupActivities をインポートします
ファイルの最後に移動し 次に新しいメソッドを実装します
ここでは 受け取った groupSessionオ オブジェクトをクラスの新しい プロパティーに割り当てました また groupSession プロパティを設定する前に キャンバスをリセットすることにも 注意してください 最後に groupSession に参加するコードを追加しましょう
この時点で プロジェクトを構築して コンパイルできるはずです ここで プロダクト>構築 の順に選択してテストします
いいですね これで グループセッションを受信して参加 するためのコードを設定したので アプリケーションでカスタム データを送受信するように セッションを設定する方法について 説明します セッションの設定には GroupSessionMessenger を使用します GroupSessionMessenger は AWデータまたは構造化された メッセージをグループセッション 内の参加者と送受信するための 簡単なAPIを提供します 次に GroupSessionMessenger の使用方法について説明します まず groupSession から GroupSessionMessenger を作成します GroupSessionMessenger を使用するための 最初のステップは アプリケーションの参加者間で 交換する必要があるデータの 種類を定義することです DrawTogetherでは 他のデバイスと 共有する必要がある特定のデータは ストロークそのものです 3つの属性を持つストロークを 表現することができます 識別子 色と座標点です UpsertStrokeMessage をCodableプロトコルに 準拠させることに注意してください これは GroupSessionMessenger が構造化された メッセージの送受信を可能にし メッセージがコーディング 可能である限り シリアライゼーションと デシリアライゼーションを 自動的に処理するためです セッションを設定するための 2番目の手順は GroupSessionMessenger のMessages APIを使用してデータを 受信することです DrawTogetherの場合 UpsertStrokeMessages の受信を処理する必要があります ここに示したメッセージAPIは コーディング可能な型を取り 非同期シーケンス その型のメッセージを含むタプルと メッセージを囲むコンテキスト そのメッセージを送信した 参加者などの情報を含む を返します セッションを設定する為の 3番目の手順は GroupSessionMessengerのsend API を使用してデータを送信します DrawTogetherの場合 グループ内のすべての参加者に UpsertStrokeMessage を送信します 送信APIは非同期スローメソッド であることに注意してください スローされるエラーは アプリケーションによって適切に 処理される必要があります 次に Xcodeに移動し GroupSessionMessengerコードを追加します まず Canvasソース ファイルに移動し Groupセッションから GroupSessionMessenger を作成します
作成したメッセンジャー オブジェクトを保持する messengerプロパティー をキャンバスに追加します
次に 参加者間で送受信される UpsertStrokeMessage を定義する必要があります このための新しいファイルを 作成します そのためには ファイル>新規>ファイル... を選択し Swiftファイル を選択します ... Modelフォルダに 貼り付けます これをMessages と呼びましょう ここで UpsertStrokeMessage を定義するコードを追加します GroupSessionMessenger で送受信するメッセージを 定義したので 送受信するコードを作成しましょう これを行うには キャンバスに戻ります
ファイルの最後に メッセージを受信するコードを 追加します
ここでは 非同期シーケンスから UpsertStrokeMessages を受信する分離タスクを作成し 新しいメソッドhandleを呼び 出してメッセージを処理します 次はそれを実行しましょう
このコードでは 識別子をチェック してストロークが既に存在するか どうかを確認し存在する場合 はポイントを追加します それ以外の場合は 新しいストロークを作成して ポイントを追加し ストロークの 配列にストロークを追加します 次に メッセージを送信する コードを作成します 上記のメソッド addPointToActiveStrokeに進みます
すごい! 今度は アプリをビルドして実行し DrawTogetherの 共有エクスペリエンスを 実際に見てみましょう 2つのデバイスで FaceTime通話を始めます Phoneアプリにアクセス してから自分に電話する
他のデバイスで応答します
マイクを消して このデバイスでは 共有DrawTogether エクスペリエンスを開始します 左下のアイコンをタップします もう一方のデバイスでは グループセッションに参加します 今度は私と tic-tac-toeをします 先に移動します
私が勝ちそうです いいね コードは機能しているようだ
ここまで GroupSessionMessenger を使用して 最初にメッセージ を定義し 次にメッセージを受信し 最後にメッセージを送信する セッションを 構成する方法について説明しました 次に その他のいくつかの 方法について説明します GroupSessionMessenger を使用するときに考慮すべきこと
内部的には GroupSessionMessenger は グループ内のすべての アクティブな参加者に 信頼性の高いFIFO順の メッセージ配信を提供します 送信するメッセージには 制約があります サイズが大きすぎると 送信APIからエラーが 表示されます GroupSessionMessenger は小さなペイロード向けであり ファイル 画像 ビデオなどの 大きなアセットのストリーミング には使用しないでください メッセージを送信するときは フローコントロールとレート リミットを考慮する必要があります ループのように連続して大量の メッセージを送信すると 送信APIからエラーが 送信されることがあります 最後に GroupSessionMessengerで 使用するメッセージを定義する時は アプリケーションプロトコルに バージョン管理サポートを追加する ことを検討してください これにより アプリケーションは古いバージョン のソフトウェアを実行している デバイスとの相互運用を サポートできます それでは この記事をAdam にお願いします Adamは GroupActivities 体験をどうに洗練させるか話します ありがとうAngus! それでは アプリのカスタム体験に 必要な仕上げについて話しましょう まず late joiner について話しましょう Late joinerは セッションの開始後に activitySession に結合するデバイスです 適切なエクスペリエンスを 確保するには すべてのデバイスが同じデータで 動作するように late joinerに最新の 情報を提供する必要があります このシナリオを考慮することは 一貫したユーザーエクスペリエンス を確保するために重要ですが 万能というわけではありません このキャッチアップ処理に 必要なデータは アプリと経験によって異なります DrawTogetherアプリに どのように適用されるか見てみよう グループセッションに2つの デバイスがあるとします これら2つのデバイスは同じ 情報を持っている キャンバスに描かれた スマイルフェイス 両方とも図面を作成したときに セッションに参加していたため 経験を通じて同じデータを 持っていることがわかります 次に 別のデバイスを追加します この時点で 新しいデバイス 呼び出しは GroupSessionに参加しますが キャンバスには何も表示されません 雲を描いて それは 良くない! 新しいデバイスには事前の コンテキストがなかったので 笑顔には雲がかかっている さあ バックアップして もう一度やってみましょう 新しいデバイスが参加するとすぐに スマイルを見せるようにするには どうすればいいですか? 新しいデバイスがグループ セッションでjoinを呼び出すと そのグループセッションに参加 している他のすべてのデバイスには GroupSession起動時に activeParticipantsプロパティが表示されます その信号を観測したデバイスは キャッチアップデータ この場合は既存の描画キャンバス を新しく接続されたデバイスに 送信します 新しいデバイスが接続されると スマイルマークが既に 表示されていることがわかるので それを囲むことができます ではどうやってコードを 書くのでしょう? 最初にすべきことは アプリがキャッチアップメッセージ で送信する必要のあるデータを 理解することです DrawTogetherの体験であり 目標はキャンバスが全員の デバイスで 同じになることを 保証することなので 先に進みメッセージの中で 新しいメッセージを作成しましょう 「CanvasMessage」 と呼ばれるswiftファイル この構造体には すべてのストロークと 「pointCount」 と呼ばれる変数が含まれます この変数は 最新のメッセージを 計算するための ヒューリスティックとして 使用されます すごい! さて このメッセージの 受信をどのように処理しますか? Canvas.swiftの Canvasモデルでは Angusが以前示したように GroupSessionMessenger を使って configureGroupSessionに メッセージハンドラを設定できます
ここからは handle関数を呼 び出していることがわかりますので それを実装しましょう
このコードでは pointCount ヒューリスティックから保護して 現在より新しいキャッチアップ メッセージだけを 受け入れるようにしています パスした場合は キャンバスのストロークを catchupMessageの ストロークでオーバーライドします 先ほど説明したように activeParticipants を変更して 新しい参加者と通信する 必要があるか どうかを判断する必要があります 次に その設定を configureGroupSession 関数に追加します
このハンドラでは 新しい activeParticipants と古い activeParticipants の差分が取得されます これにより 新しく参加した参加者 にのみキャッチアップメッセージを 送信できます すごい! 今 私たちはメッセージを作成して 送信するだけです
このメッセージには現在の キャンバスの状態が含まれ newParticipants にのみ送信されます 以上です キャッチアップだ グループセッションで特定の アクティビティを を行うためのすべての要素が 揃ったところで アクティビティを完全に変更する にはどうすればよいでしょうか たとえば 描画キャンバスを 変更したり ムービーを変更したりできます このAPIには アクティビティを 変更する2つの方法があります 新しいグループセッションを 作成するか 既存のグループセッションの 全員のアクティビティ を更新することができます では その2つについて 話しましょう コンテンツを変更する最初の 方法として推奨されるのは グループセッションを開始したのと 同じAPIを呼び出す方法です GroupActivityに対する prepareForActivation この方法では GroupSessionの開始と 終了のための クリーンなバリアが提供されるため 以前のGroupSessionから 不要な状態やメッセージが 残ってしまうことを 心配する必要がないため 参加者間で状態が 一貫しているかどうかを 判断するのが容易になります これは ユーザーがアクティビティ から戻って 新しいメモや映画を検索するなど 次のアクティビティを 見つける場合に非常に便利です これにより システムに重大な変更が示され ユーザーへの通知に使用されます この呼び出しの後 グループセッションを開始するのと 同じ方法で GroupActivity上の セッション非同期シーケンスを 通じて新しいGroupSession を受信します では 例えば複数の曲を 交互に演奏するなど アプリケーションが移行する アクティビティーのリストを 持っているとしたらどうでしょう GroupSession API はGroupSessionの アクティビティープロパティを 設定するだけで すべてのユーザーに対して更新を トリガーする簡単な方法を提供します 次に アクティビティ プロパティの変更を確認します このAPIは デバイスが常に同じアクティビティに 収束するようにするので 心配する必要はありません この2つを概念的に理解したところで どちらを使うべき DrawTogether アプリのために? 私たちのアプリは新しい 描画キャンバスの上に クリーンなスレートを 望んでいるので New Session APIは 望むものを正確に提供してくれます それでは Xcodeに移り その実装方法を見てみましょう ここでの最初のステップは 新しいセッションをトリガーする 方法を決定することです ここでは クライアントがリセットボタンを 使用したときに新しい GroupSessionを 作成するようにします ControlBarコードを 見ると Canvasモデルを呼び出して ローカル状態をリセットする CapsuleButtonが既に あることがわかります そこで この関数を変更して GroupSessionを破棄し 新しいGroupSession を作成しましょう
このコードでは GroupSessionにある タスクとキャンセル可能なものを すべてキャンセルします また GroupSession があるかどうかも確認します その場合は そのままにして DrawTogetherタイプの activateプロパティー を呼び出します そこから 通常のGroupSession 受信フローが実行されます これで新しいキャンバスに 簡単に移行できます では SharePlayの体験を友達と 試すことができることを ユーザーに示すようにUIを 変更したい場合は? 例えば 描画アプリケーションでは キャンバスをこの状態から この状態に変更し GroupSessionの 対象になると キャンバスを共有するための ボタンが表示されます ではどうすればいいのでしょう? GroupStateObserver APIを使用すると デバイスがグループセッションに 適しているかどうかを パブリッシャから確認できます これを使用して ボタンを動的に表示および 非表示にできます では実行しましょう 先ほど見たように アプリケーションの左下に 小さなボタンが必要です アプリケーションの ControlBarビューには すでに共有ボタンがあるので GroupStateObserver に基づいてボタンの 表示/非表示を切り替えるように 動作を変更します まず ビューに groupStateObserver を追加します
次に CapsuleButtonを囲み groupSessionに 適格であるかどうかだけを表示し このgroupSession にまだ入っていないことを示します
以上です! ボタンが動的に表示されるのは ユーザーの役に立つとき だけになりました では このセッションで見たことを 見てみましょう 私たちはシンプルな描画アプリを 作る全プロセスを終え Group Activities を活用してこれまでにないように 同期して接続できるように 変更しました しかし さらに重要なことに 私たちは あなたの創造性を完全に 解き放ち Group Activitieで カスタムSharePlay体験を 作成するために必要なすべての ステップを実行しました ここでは汎用タイプの カスタムアクティビティの作成 GroupSessionと GroupSessionMessenger の同期通信の設定と活用 そしてアプリケーションが真に リッチなユーザエクスペリエンスを 実現するために 採用すべきエッジケースと APIについて説明しました このカスタム体験を楽しんで 頂けた事を願っています 皆さんの創造性がグループ活動 フレームワークに活きることを 楽しみにしています GroupActivities について学ぶ次のステップは 「Design for Group Activities」セッションです まだご覧になっていない方は 「Build media experiences with Group Activities」 セッションもご覧ください ご不明な点がございましたら グループ活動ラボに お問い合わせください 最後に ご覧いただいて ありがとうございます WWDCを楽しんでください 何を作るのか楽しみです! (音楽)
-
-
3:50 - Configuring your application’s activity
struct DrawTogether: GroupActivity { var metadata: GroupActivityMetadata { var metadata = GroupActivityMetadata() metadata.title = NSLocalizedString("Draw Together", comment: "Title of group activity") metadata.type = .generic return metadata } }
-
10:06 - Define, Receive and Send messages
let messenger = GroupSessionMessenger(session: groupSession) // 1. Define struct UpsertStrokeMessage: Codable { let id: UUID let color: Color let point: CGPoint } // 2. Receive for await (message, context) in messenger.messages(of: UpsertStrokeMessage.self) { // Handle message } // 3. Send do { try await messenger.send(UpsertStrokeMessage(id: stroke.id, color: .red, point: point)) } catch { // Handle error }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。