ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
AppインテントによるAppショートカットの実装
ユーザによる設定なしで、みなさんのAppのショートカットを作成する方法をご確認ください。Appインテントでカスタムのショートカットビューを表示する方法や、パラメーター化されたフレーズのサポートを追加して、ユーザの思い通りに素早く意図を表現する方法を紹介します。また、Siri Tipやショートカットリンクを使用して、Appショートカットを見つけやすくする方法も紹介します。 このセッションを最大限活かしていただくためには、SwiftUIに関する基本的な知識を習得されていることが推奨されます。
リソース
関連ビデオ
Tech Talks
WWDC22
-
ダウンロード
♪ 落ち着いた雰囲気の ヒップホップ音楽 ♪ ♪ こんにちは Michael Sumnerです Siri とAppインテントの ソフトウェアエンジニアです このセッションでは 新しいApp Intents フレームワークを使い Appショートカットを 作成する方法を説明します 最初にショートカットの 概要と Appインテントとの 関連性を説明します 次に Swift を使用して Appショートカットを作成し パラメータを追加する方法を お見せします 最後にユーザがあなたの 努力から利点を得られるよう Appショートカットを発見可能に する方法を説明します ではApp Intents フレームワークと Appショートカットから 始めましょう ユーザはショートカットAppや Siriから使用可能な みなさんのAppを利用した 複数ステップのワークフローを 作成するために ショートカットを使用します これまでは インテントを 使用可能にするために 「Siriに追加」 ボタンによる設定か ショートカットAppでの 設定が必要がありました そこでユーザ設定が まったく不要な 新しい Appショートカットを 発表します これでユーザが みなさんのショートカットから メリットを得やすくなります Appショートカットを サポートすることで Appで定義されたインテントは Appがインストールされた直後から 利用可能になります これにより ユーザは みなさんのAppの機能を 発見・使用しやすくなります 何かを設定するために ショートカットAppや 「Siri に追加」ボタンを 使用する必要はなくなります ユーザが構築する ショートカットのように ショートカットApp Spotlight Siri で 実行できます これでユーザは複数の方法で システム内の異なる場所から Appを発見し やりとりすることができます 例えば Spotlightで検索する場合 検索結果に Appショートカットが 表示され アクセスしやすくなります Appショートカットを 実装することで ユーザーはみなさんのAppで すばやく軽いやりとりをして タスクを完了し 目的の達成へと 向かいやすくなります 私のチームは 一連の 音声によるガイドや サウンドを使い ユーザが瞑想し 重要な ことに集中するのに役立つ 「Mediation」という Appを作成しています 現在 瞑想を始めるには ユーザはAppを起動し ログインして実施する瞑想 セッションを検索します Appショートカットと サポートすれば ユーザは Siri にリクエスト するだけで どこからでも Appにアクセスできるように なります セッションを すばやく開始することで ユーザは朝の仕事前や 長い1日の緊張をほぐすため 夕方に瞑想することを 日常生活の一部に 取り入れることができます では Appインテントを作成し それをAppショートカットに するために必要な コードについて説明します 以前のショートカットとは異なり Appショートカットは 新しい App Intents フレームワークで構築します App Intentsフレームワークは 優れたインテントを すばやく簡単に構築するための 一から構築された Swiftオンリーの新しい フレームワークです Appインテントでは すべてが Swiftのソースコードで 定義でき 別のメタデータ ファイルは必要ありません これはすべてのコード生成 手順を省略でき ソースエディタと メタデータエディタの間で コンテキストを切り替える 必要がなく 集中しやすくなり コードのレビューや マージの競合の解決も 行いやすくなります Appショートカットを 構築するには Appインテントを本格的な ショートカットに変えるのに 必要なフレーズや 他の メタデータをリストする AppShortcutsProviderを 書く必要があります ここで注意が必要なのは Appショートカットは ユーザーインタラクションなしで 設定されるため トリガーフレーズに App名を含む必要がある点です インテントは AppIntentプロトコルを実装した Swiftの構造体で 定義されます 基本のインテントの 要件は2つだけ ショートカットAppに 表示されるタイトルと performと呼ばれる メソッドです performメソッドにて インテントのロジックを実装し 結果を戻すようにします さらに ユーザにプロンプトを出し レスポンスを待つこともできます このインテントではAppの MeditationServiceで デフォルトの 瞑想セッションを始めます performメソッドは 非同期のため 非同期コードを実行し セッションを開始できます セッションが開始したら ユーザに表示される ダイアログを戻します Appが ローカライズされている場合 すべての地域でこの文字列を ローカライズします ここまでの構築によって ショートカットを設定しようとすると StartMeditationIntentが ショートカットAppに現れます やる気のあるユーザは このインテントを使用し セッションを開始するための ショートカットを作成できます このショートカットでは 2つ目のインテントを作成し 「集中」を有効にします デフォルトでは インテントはソースコードで 指定したタイトルで レンダリングされます アクションのレンダリングを カスタマイズするには Appインテントに パラメータサマリを追加します パラメータサマリでは インテントの見た目を カスタマイズでき インラインで値を表示します このインテントは ショートカットになりますが ユーザが ショートカットの設定なしで インテントを実行できるのが 理想的です Appショートカットを 作成すれば この設定手順をユーザの 代わりに行うことができ ユーザはAppを インストールした途端に 私のインテントを 使用できるようになります では インテントを書いたところで Appショートカットを 作成しましょう インテントと同様に Appインテントも AppShortcutsProvider プロトコルを実装して Swiftコードで定義します プロトコルを実装するには ユーザのために設定したい すべてのショートカットを 返す単一のゲッタを 作成するだけです みなさんのAppにおいて 最大10件の Appショートカットを 持たせることができます しかし ほとんどのAppは 数件必要なだけでしょう StartMeditationIntent 用に 単一の AppShortcut を 作成します 始めにインテントの インスタンスを渡します イニシャライザに パラメータが必要な場合は ここで値を指定します 次に Siri から Appショートカットを起動する 一連のフレーズを作成します 文字列に直接App名を 入力する代わりに 特別な.applicationName トークンを使用します これにより Siri は Appのメイン名に加え 構成したApp名の シノニムを挿入できます ユーザは 異なるフレーズを 使って瞑想を起動するため 代替のフレーズを もう少し追加します Appがローカライズ されているなら これらのフレーズも ローカライズしてください
完了しました これで ユーザが瞑想を始める時 Siri に「瞑想をスタート」と 言えばAppが起動します Siri は StartMeditationIntentをコールし 私が返したダイアログを 読み上げます また 誰かが Spotlightで 私のAppを検索すると 私がコードに記載した 最初のショートカットが 表示されます ユーザーが 結果をタップすると Appを起動せずに ショートカットが すぐに実行されます ただしインテントが Appの起動をトリガーする場合 Spotlightには表示されませんので ご注意ください これだけのコードで ユーザが私のAppで 瞑想するのが 非常に簡単になりました しかしこのままでは インテントを実行すると Siri はデフォルトの ビューを表示します これでも構いませんが ユーザがショートカットを 実行した場合に詳細な 情報を表示したいとします それには インテントを 実行した際に Siri が それを表示するようカスタム ビューの実装が必要です App Intentsフレームワークの ビューは SwiftUI で構築され ウィジェットと同じ技術を 使用するため カスタムビューのために 別の UI Extension を 使用する必要はありません 代わりに インテントを実行する際に ビューを返すだけです ビューにもたらされる 特定の制約について 意識することが重要です ウィジェット同様に このカスタマイズされたビューは インタラクティビティやアニメーションを 含むことはできません UIをデザインする際に この事を忘れないでください インテントでは3つのフェーズで カスタム UI を表示できます 値の確認フェーズ インテントの確認フェーズ インテント終了後です 私のAppでは インテント実行終了時に カスタムビューを返します 他のプロンプトを 使用する場合は これらのフェーズで カスタム UI を サポートする方法も 必ず考慮しましょう 先述のように カスタム UI の表示は簡単です インテントからビューを 返すだけです ではコードに移りましょう カスタムビューの追加は 簡単です ダイアログと共にビューを 返します App Intentsフレームワークは Siriスニペット内で ビューを提示します このビューは スニペットのタイトルや 確認ボタンなど他の Siriのビューと一緒に 表示されることを 忘れないでください そのためスニペットが Siriの中で ホームであるように感じられる デザインをします 次はAppショートカットを 拡張し パラメータを含める方法を お見せします 前回の実装では デフォルトの瞑想の 開始を選択しましたが Appにはすばらしい セッションが他にも多くあり ユーザは特定のセッションを 開始したい場合が 考えられます ユーザが私のインテントを 実行した場合に セッションを指定できるのが 理想的です このユースケースに 対応するためには ユーザーが実行したい セッションを取得する パラメータを追加して インテントを拡張します パラメータを追加するには まず パラメータの型を 定義します セッションの関する 情報がある MeditationSession 構造体を作成します 名前を含み identifier フィールドを 提供します これを UUID に することができます この構造体をインテントの パラメータとして使うには AppEntityプロトコルを 実装する必要もあります AppEntityプロトコルを 実装すると App Intentsフレームワークに 独自の型を伝え エンティティの表示方法など 追加の情報を 指定することができます エンティティプロトコルでは 型に対する識別子が必要ですが ここでは すでに提供しています 整数や文字列など 他の型も使用できます エンティティの 表示方法について 情報を 提供する必要もあります これはエンティティが 表示される ショートカットAppや その他の場所で使用されます 最後にデフォルトクエリを つなげる必要があります クエリ名は MeditationSessionQuery これを次に実装します App Intentsフレームワークが エンティティと連動するには エンティティの識別子に 基づいて 検索できる必要があります これを可能にするために EntityQuery プロトコルは 次の1つの要件を定義します 識別子を使い 一致するエンティティを 返す関数です SessionManagerで このセッションを検索し この関数を実装します StartMeditationIntentを 更新して パラメータを追加します パラメータは簡単です 構造体の 通常のプロパティです しかしAppインテントに パラメータを伝えるには @Parameter Property Wrapperの追加が必要です このProperty Wrapper は Appインテントに セッションプロパティが インテントの一部だと伝え 表示名など 追加のメタデータを @Parameter Property Wrapper で指定できます パラメータプロパティを インテントに追加したら どのセッションを実行するか ユーザに聞く必要があります App Intents フレームワークには インテントのパラメータの 値を得るために ユーザに補足の質問ができる 堅牢なサポートがあります これらの指示はインテントが 実行される場所で表示され Siri から実行される場合であれば Siri が質問を読み上げ ユーザは答えを言うように 指示されます Spotlight と ショートカットAppでは ユーザーは タッチドリブンなUIで 同じプロンプトが出ます Appインテントに対応する 値を得るプロンプトは3つ 曖昧性解消はリストからの 選択を指示します 曖昧性解消は インテントのパラメータが 一定のオプションに 限定される場合に 最適です 値のプロンプトでは 制限のない値を ユーザに聞くことができます これは任意の値を使用できる 文字列や整数に 最適です 最後に 確認は ユーザに特定の値の確認を要求し ユーザがインテントを 理解していることを 再確認するのに 最適です 値のためのプロンプトは インテントの 柔軟性を高め ユーザからより多くの 情報を得るのに すばらしい方法です しかし会話の速度を下げ 頻繁に使用するとユーザが 苛立つ場合があります 優れたインテントの作成の 詳細については Lynnによる 『Appショートカットの デザイン』をご覧ください ではセッションパラメータを StartMeditationIntentに 追加したところで performメソッドに ロジックを追加して この値をプロンプトします 私のAppにはユーザが 実行できる 一定数の セッションがあります セッションがまだ 指定されていない場合 SessionManagerから リストを取得し ユーザーに曖昧性解消を 示します 各セッションにDisplay Representationを使用すると Appインテントは セッションをリスト項目にし ユーザに表示します ユーザが1つ選択すると 選択した項目が 戻されます 選択されたセッションが MeditationServiceに渡され セッションが開始します 次にユーザーに インテントが開始したことを 知らせるダイアログを 返します ユーザがセッションを 渡したので ダイアログに セッション名を表示し リクエストを把握したことを 知らせるのがよいでしょう 今ユーザが「瞑想をスタート」 と言っています Appはユーザが 開始したいセッションに 移動します ただし 先述のように ユーザはすばやく明確な Siri とのやりとりを望み 理想的には 補足的な質問ではなく 初期の段階でユーザーが 好みのセッションを 伝えられることが 好ましいです ここでいいニュースが あります Appショートカットは 事前定義されたパラメータで トリガーフレーズを 拡張することができます パラメータ化された フレーズの実装により Appは 「落ち着く瞑想をスタート」や 「歩く瞑想をスタート」などの 発言に対応することができます パラメータは Siri に 事前に指定できる 一定数の決まった パラメータ値がある場合に 役立ちます 私のAppでは セッション名を使用します パラメータは制限なしの値で 使用するものではありません 例えば最初の語りかけで ユーザから任意の文字列を 収集することはできません そのためAppは Xがユーザの任意の 入力となる場合 「Xの日記を検索」などの 語りかけに 対応することはできません Appの実行中より前の タイミングで パラメータ値を事前に 指定する必要があります。 パラメータ化されたフレーズを 実装してみましょう Appでこれを行う場合 いくつかの変更を 行う必要があります まず suggestedResults() メソッドを実行し ショートカットに エンティティのリストを 返すためにSessionEntityの クエリを更新します 次にApp Intents フレームワークに SessionEntitiesが 変更したことを伝えます これによりApp Intents フレームワークは Siri で使用される 新しいフレーズを作成します これを行うには Appのモデルレイヤを更新し セッションリストが 変更されるたびに App Intentsフレームワークに 通知されるよう設定します 最後に StartMeditationIntentで セッションパラメータを 参照するAppショートカットに 新しいフレーズを いくつか追加します まず suggestedEntities 関数を実装し MeditationSessionQueryを 更新します App Intentsフレームワークは 関数から戻されたセッションで パラメータ化された ショートカットを作成します このメソッドは オプショナルでして このメソッドを実装しない場合 パラメータ化された ショートカットは 利用できません またセッションのリストが 変更された場合 それを App Intentsフレームワークに それを知らせるために Appのモデルレイヤを 更新する必要があります ここではバックグラウンドで サーバから取得した 新しいセッションタイプを 不定期に公開しています SessionModelを更新して 新しいセッションを 受信するたびに update AppShortcutParameters()を 呼び出します これはApp Intents フレームワークが提供し 自身で実装する必要はなく Appインテントは エンティティの クエリを呼び出し ショートカットフレーズの パラメータを収集します 最後にAppショートカットに インテントのセッション キーパスを含む 新しいフレーズを追加します フレームワークは このフレーズを クエリから返されるすべての セッションと組み合わせます 各値で使用されるテキストは SessionEntityの Display Representationで タイトルプロパティから取得されます 先ほどのように ユーザが 私にAppショートカットに 使う異なるフレーズを数件 追加したいと思います これで ユーザがみなさんが 設定したフレーズを 思い出せない時でも スムーズな体験を確保します これで完全に機能する ショートカットができました ユーザが使用するのが 待ち遠しいです しかしそうなるためには ユーザがショートカットを 発見する方法が必要です まずお伝えしたいのは良い フレーズを選択すること これは短くて覚えやすい フレーズを意味します ユーザのiPhoneには App ショートカットに対応する 多くのAppが インストールされるはずです そのためユーザが ショートカットのフレーズを 思い出せないことが 考えられます なので短くて簡単な フレーズにしましょう それに加えApp名が 名詞または動詞に できる場合は フレーズでそれらを 使用するようにします このAppではフレーズを 短く覚えやすくするために 瞑想を 名詞として使っています またApp名のシノニムは ユーザにとって便利です ユーザがAppを呼ぶ際に Appの表示名以外で 呼ぶ場合があるので Appのシノニムを 追加することを推奨します iOS 11から App名の シノニムに対応しています まだ作成していないなら 今がチャンスです 次にお話ししたいのは Siri Tipと ショートカットリンクです Appショートカットは ユーザー設定が必要ないので ユーザがみなさんの Appショートカットを 発見・使用するために 見つけやすさは重要です Appショートカットでは ユーザーはショートカットの 追加のために 「Siriに追加」ボタンが不要です すでに追加されているためです しかし「Siriに追加」 ボタンが提供する 見つけやすさを 失いたくありません それを考慮して新しい Siri Tipビューを作成しました このビューは過去に 「Siriに追加」ボタンを 使用していた場所で うまく機能します TipビューはSwiftUIと UIKitで使用することができ さまざまな スタイルが利用可能です どのAppでもTipが すばらしい形で見えます Siri Tipは 画面のコンテンツに 関連性がある場合 状況に応じて配置します ユーザがみなさんのAppで 注文したところなら 注文状況を示す ショートカットの Tipを表示することを 考慮します Siri Tipは ユーザが近い将来 Appショートカットを 使うだろうと感じた時に それを十分に考慮して配置します Siri Tipは消すことにも 対応します ビューには消すためのボタンが 含まれ コードが タップされた時にカスタムの クロージャがトリガーされます レイアウトからビューを 削除して 関連性があると感じるまで 表示しないことを 考慮してください ショートカットの リストを起動する ショートカットリンクが 追加されました この新しい要素は Appに多数の ショートカットがあり ユーザにすべて探索して ほしい場合に便利です Appショートカットの すばらしい点は Appをインストールして すぐに利用できることです Appが最初に起動する前に ユーザはショートカットを Spotlight Siri ショートカットAppから 実行し確認できます Appショートカットを 構築する時は この事を配慮してください 例えばAppに ログインフローが必要な場合 ユーザがインテント実行前に ログインしていなければ インテントは失敗し ユーザのログインが必要だと 説明するメッセージが 表示されます 次にAppショートカットの パラメータ化されたフレーズは Appが起動するまで 利用することはできず App Intents フレームワークに 新しいパラメータ値が あることを伝えます Appショートカットに パラメータ化されていない フレーズが含まれていると Appを起動するまで ショートカットは 表示されません これを避けるために いくつかの パラメータ化されていないフレーズを 追加することを おすすめします さらに Siri は 「ここでなにをしますか」 「瞑想で何ができますか」 などのフレーズに対応します Siri はショートカット フレーズを自動的に収集 推薦し みなさんの代わりに 提示します この機能が動作するために Appが追加で 何かをする必要はありません Siri とショートカット Appの両方で ショートカットが表示される 順番は ソースコードに記載した ショートカットの順番で 表示されます 最も良い便利な Appショートカットを 最初に並べて 注意を引くようにしましょう 同様にフレーズの配列に 最初に載せるフレーズは Appショートカットの プライマリのフレーズとして ショートカットタイルの ラベルとして使用されます またユーザが Siri に Appへのヘルプを 依頼した時に表示されます App Intentsフレームワークと ショートカットで 多くのご案内をしました 最後に2つの主要な 考えをお伝えします Appショートカットは ユーザがシステムのあらゆる 場所からAppを 使用できるようになるので この軽量なモデルに最適な ユースケースを考えましょう 次にAppショートカットを 実装したら ユーザにそのことを 伝える必要があります 見つけやすくするための 方法を熟考してください Siri Tipや Webサイトや店舗のサインなど プロダクト外の場所での 表示を考慮してください 新しいApp Intents フレームワークで 作成するショートカットを 見るのが楽しみです デザインについての詳細 App Intents フレームワークの詳細は 今週の別のトークを ご覧ください ありがとうございました WWDCをお楽しみください ♪
-
-
3:43 - Implement an AppIntent
// StartMeditationIntent creates a meditation session. import AppIntents struct StartMeditationIntent: AppIntent { static let title: LocalizedStringResource = "Start Meditation Session" func perform() async throws -> some IntentResult & ProvidesDialog { await MeditationService.startDefaultSession() return .result(dialog: "Okay, starting a meditation session.") } }
-
5:31 - Create an AppShortcutsProvider
// An AppShortcut turns an Intent into a full fledged shortcut // AppShortcuts are returned from a struct that implements the AppShortcuts // protocol import AppIntents struct MeditationShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: StartMeditationIntent(), phrases: ["Start a \(.applicationName)"] ) } }
-
6:35 - Provide multiple phrases
// An AppShortcut turns an Intent into a full fledged shortcut // AppShortcuts are returned from a struct that implements the AppShortcuts // protocol import AppIntents struct MeditationShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: StartMeditationIntent(), phrases: [ "Start a \(.applicationName)", "Begin \(.applicationName)", "Meditate with \(.applicationName)", "Start a session with \(.applicationName)" ] ) } }
-
8:54 - Provide a dialog and snippet view
// Custom views give your intent more personality // and can convey more information func perform() async throws -> some ProvidesDialog & ShowsSnippetView { await MeditationService.startDefaultSession() return .result( dialog: "Okay, starting a meditation session.", view: MeditationSnippetView() ) }
-
10:09 - Implement an AppEntity
// An entity is a type that can be used as a parameter // for an AppIntent. import AppIntents struct MeditationSession: AppEntity { let id: UUID let name: LocalizedStringResource static var typeDisplayName: LocalizedStringResource = "Meditation Session" var displayRepresentation: AppIntents.DisplayRepresentation { DisplayRepresentation(title: name) } static var defaultQuery = MeditationSessionQuery() }
-
10:55 - Query for entities
// Queries allow the App Intents framework to // look up your entities by their identifier struct MeditationSessionQuery: EntityQuery { func entities(for identifiers: [UUID]) async throws -> [MeditationSession] { return identifiers.compactMap { SessionManager.session(for: $0) } } }
-
11:16 - Define a parameter
// Adding a parameter to an intent allows you to prompt the user // to provide a value for the parameter struct StartMeditationIntent: AppIntent { @Parameter(title: "Session Type") var sessionType: SessionType? // ... }
-
13:15 - Prompt for values
// Prompting for values can be done by calling methods // on the property's wrapper type. func perform() async throws -> some ProvidesDialog { let sessionToRun = self.session ?? try await $session.requestDisambiguation( among: SessionManager.allSessions, dialog: IntentDialog("What session would you like?") ) } await MeditationService.start(session: sessionToRun) return .result( dialog: "Okay, starting a \(sessionToRun.name) meditation session." ) }
-
16:11 - Implement suggestedEntities()
// Queries can provide suggested values for your Entity // that serve as parameters for App Shortcuts struct MeditationSessionQuery: EntityQuery { func entities(for identifiers: [UUID]) async throws -> [MeditationSession] { return identifiers.compactMap { SessionManager.session(for: $0) } } func suggestedEntities() async throws -> [MeditationSession] { return SessionManager.allSessions } }
-
16:34 - Update App Shortcut parameters
// Your app must notify App Intents when your values change // This is typically best done in your app’s model layer class SessionModel { @Published var sessions: [MeditationSession] = [] private var cancellable: AnyCancellable? init() { self.cancellable = $sessions.sink { _ in MeditationShortcuts.updateAppShortcutParameters() } } // ... }
-
17:09 - Add parameterized phrases
// Phrases can also contain a single parameter reference import AppIntents struct MeditationShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: StartMeditationIntent(), phrases: [ "Start a \(.applicationName)", "Begin \(.applicationName)", "Meditate with \(.applicationName)", "Start a \(\.$session) session with \(.applicationName)", "Begin a \(\.$session) session with \(.applicationName)", "Meditate on \(\.$session) with \(.applicationName)" ] ) } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。