View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

その他のビデオ

  • 概要
  • トランスクリプト
  • コード
  • App Intentの最新テクノロジーの詳細

    今年のリリースでApp Intentフレームワークに追加されたすべての新しい機能強化を紹介します。デベロッパの生産性向上に役立つ機能強化(遅延プロパティなど)、および新機能(インタラクティブなApp Intentスニペット、エンティティビューの注釈、ビジュアルインテリジェンスの統合など)について説明します。これまで以上に表現力が高まると同時に、より簡単かつスムーズに導入できるようになったApp Intentについても詳しく説明します。さらに、今年App Intentの対象に追加された新しいエキサイティングなクライアント(Spotlight、ビジュアルインテリジェンスなど)を紹介し、それらのコンテキストでスムーズに動作するApp Intentの作成方法についても解説します。

    関連する章

    • 0:00 - イントロダクション
    • 0:55 - インタラクティブスニペット
    • 8:15 - システム統合のための新機能
    • 15:01 - より高度なユーザー体験
    • 21:02 - 便利なAPI

    リソース

    • Accelerating app interactions with App Intents
    • Adopting App Intents to support system experiences
    • App intent domains
    • App Intents
    • App Shortcuts
    • Building a workout app for iPhone and iPad
    • Creating your first app intent
    • Integrating actions with Siri and Apple Intelligence
    • Making actions and content discoverable and widely available
    • PurchaseIntent
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC25

    • インタラクティブなスニペットのデザイン
    • App Intentの基礎知識
    • App Intentを使用したショートカットおよびSpotlight向け機能の開発

    WWDC24

    • アプリをSiri対応にするための方法
    • システムの利便性を高めるApp Intentのデザイン
    • App Intentでアプリのコア機能をユーザーに提供
    • App Intentの新機能
  • このビデオを検索

    こんにちは App Intentチームの エンジニア Jeffです 今日は App Intentの 新たな進歩について紹介します

    App Intentによって 皆さんは ショートカット、Spotlight Visual Intelligenceなども含め 使用するデバイス全体に アプリの機能を 組み込めるようになります

    フレームワークに詳しくない方は まず 「App Intentの基礎知識」を ご覧になることを お勧めします こちらのビデオは いつでもご覧いただけます 今回お伝えする内容は インタラクティブスニペットで構築できる 新しい体験や アプリによって システム全体で App Intentを活用する 様々な方法や アプリのユーザー体験を洗練させるために 使用できる その他の機能や デベロッパ体験の向上をめざして追加された 便利な各種APIなど 盛りだくさんです では インタラクティブスニペットの 紹介から始めましょう

    スニペットでは App Intentにより 確認の要求や 結果の表示を アプリに合わせたビューで 実行できます インタラクティブ性も加わり スニペットに 命を吹き込めるようになりました 例えば ガーデニングスニペットを 構築して 土が乾いたらスプリンクラーを オンにするよう提案させたり 料理の注文をまとめてから レストランに送ったり ライブアクティビティなど 他のシステム機能と統合して スコアの確認後すぐに 試合を フォローするのもいいですね サンプルアプリを使って インタラクティブスニペットの 簡単なデモをお見せします その後 実装について説明します

    TravelTrackingアプリには 世界中の観光名所が収録されています タップしてAppIntentを実行し 最寄りの観光名所を探します

    観光名所が見つかると スニペットが表示されます タイトルの横には ハートボタンがあります 自分はトロント出身で ナイアガラの滝は 自分の庭のようなものです ですから ハートボタンをタップして お気に入りに追加します

    スニペットはすぐに更新され 新しいステータスが表示されます 構築には 新しいSnippet Intent プロトコルが採用されています これらインテントは パラメータに従って ビューや アプリのステータスをレンダリングします アクションやリクエストの確認後は ステータス情報をもとに結果を表示できます まずは resultスニペットの 仕組みを紹介します アプリ内のインテントは パラメータ値を収容したスニペットの インテントを 結果の一部として 返すことができます スニペットの更新が必要になるたび システムはこれを使用します 更新の際システムは デベロッパが指定した値を スニペットインテントの パラメータに入力します アプリエンティティの値は クエリから取得します

    システムは スニペットインテントの performメソッドを実行します インテントはパラメータと アプリのステータスにアクセスして ビューをレンダリングし 結果の一部として返します ビューはボタンやトグルを アプリの 任意のApp Intentに関連付けできます

    この例では ハートボタンが Update Favoritesインテントを実行し チケット検索ボタンが Find Ticketsインテントを実行します スニペットのためだけに インテントを作成する必要はありません 既存のものを変更せずに 再利用できます

    ハードボタンをタップすると システムは 対応するインテントを実行して 完了するまで待ちます これで お気に入りのステータスが 最新の状態になります 完了するとシステムは 先に返してある パラメータ化された スニペットインテントを使用して 別の更新をトリガします 前と同じように 指定した値を パラメータに入力します アプリエンティティも 再度 クエリから取得します 次に システムは performメソッドを実行し ここで スニペットインテントが 更新されたビューをレンダリングします この例では ハートのアイコンが 塗りつぶされました ビューで行った変更は SwiftUIの contentTransition APIに従って 実装されます スニペットが消えるまで このサイクルが続けられます

    スニペットインテントの returnを実装してみましょう まず 既存のインテントを使用して ShowsSnippetIntentを 戻り値の型に追加します 次に resultメソッドの 新しいパラメータを使用して スニペットインテントを提供します これにより スニペットインテントを使用するたびに 指定した観光名所を パラメータに 入力するよう システムに伝えます

    次は スニペットインテント自体を 実装しましょう 大前提として SnippetIntentプロトコルに 準拠している必要があります 次に ビューを正しくレンダリングするため 必要な変数を追加します パラメータとしてマークする必要があり そうしておかないと システムには入力されません これはApp Intentですから AppDependenciesにもアクセスできます performメソッドの戻り値の型に ShowsSnippetViewを追加します 本文で メソッドを使用して ビュー生成に必要なステータスを取得し resultメソッドで viewパラメータを使用して それを返します

    SwiftUIビューの場合と同様に スニペットインテントが作成され ライフサイクル中に何度も実行されます ですから アプリのステータスを 変更しないようにしてください ダークモードへの切り替えなど デバイスの変更に反応して システムが何度か 実行することもあり得ます また ビューのレンダリングは 迅速に行うようにして 反応が悪くなるのを避けましょう

    パラメータ値の指定は 一度しか行わないため App Entitiesにのみ使用してください これらは毎回照会される 基本型の値であり 決して変更されません 他のすべての値は performメソッドで取得する必要があります

    スニペットが作成できたら SwiftUIビューがインテントを トリガする仕組みを紹介しましょう

    ビューの本文で LandmarkViewは Buttonイニシャライザを使用して 対応するApp Intentとの 関連付けを行います トグルにも 同様のAPIがあります

    これらAPIは インタラクティブな ウィジェットとともに アニメーションを カスタマイズする修飾子 contentTransitionと並んで導入されました

    詳細をについては 2023年の Lucaの講演をご覧ください それでは 確認スニペットを使って チケット検索機能を実装しましょう

    Find Ticketsボタンをタップすると チケットを何枚検索したいか 尋ねてきます 友人が一緒のため 4枚に増やしてから 検索を開始します 最終的に 結果スニペットで見つかった 最安値が表示されます では 順を追って説明します 結果スニペットからスタートすると ボタンやトグルで スニペットを提供する インテントをトリガさせると 元のスニペットが置き換わります オリジナルが結果示していた場合にのみ これが機能する点に 注意してください フォローアップインテントは いずれかのスニペットを提示できますが 提示できますが 今回はリクエストの確認で 構成ビューを示します 実行するには requestConfirmation メソッドを呼び出します アクション名をカスタマイズして スニペットインテントを指定すると 確認作業に便利です

    スニペットがキャンセルされると このメソッドはエラーをスローします キャッチはせずに performメソッドを終了させます

    これで ビューとの インタラクションはすべて resultスニペットと同じ 更新サイクルを経由します

    この例では スニペットインテントは常に 更新後のチケット数を示します どういう仕組みでしょう? 検索リクエストをAppEntityとして モデル化したので このパラメータに入力する際 システムは常にクエリから 最新の値を取得するようになります これで ビューに渡すだけで 自動的に最新の値を 取得できます

    スニペットが見えている限り システムはアプリを終了させないので ステータスを存分にメモリに保持できます データベースへの格納は不要です

    すべてのインタラクションの後で 最終的に検索アクションをタップすると 元のApp Intentの実行が 再開されます

    新しいステータスが発生したため タスクの途中で スニペットの更新が 必要になる場合があります

    このシナリオの場合は シンプルに 更新を要するスニペットインテントで 静的なリロードメソッドを呼び出します

    最高のインタラクティブスニペットを デザインする方法については こちらのセッションをご覧ください スニペットは ほんの始まりに過ぎません システム全体にアプリを 統合する上で App Intentが 他にもどういう具合に役立つかを 紹介します 第一は 画像検索です この機能により カメラのキャプチャや スクリーンショットから 直接検索を実行できます iOS 26の新機能として アプリの検索結果も表示されます これは観光名所のスクリーンショットです 検索を実行するには 画像を強調表示します すると システムが検索パネルを表示します TravelTrackingアプリを選択すると 検索結果が表示されます 結果をタップすると 対応する観光名所のページを 表示するアプリが開きます

    画像検索をサポートするには IntentValueQueryプロトコルに 準拠するクエリを実装します SemanticContentDescriptor型を 入力として受け取り App Entityの配列を返します Image Searchは固有の DisplayRepresentation型で AppEntityを表示します

    結果をタップすると 対応するAppEntityが OpenIntentに送られ アプリで処理されます このOpenIntentは不可欠で これがないとアプリは表示されません

    クエリを実装するには IntentValueQueryプロトコルに 準拠している構造体から スタートします valuesメソッドへの 入力として SemanticContentDescriptorが必要です 選択した領域のピクセルが ここに格納されます CGImageなどの 馴染みのある型に変換するには Video Toolboxや CoreImageのAPIを使用できます

    検索を実行した後 条件に合致したエンティティの 配列を返します 次に OpenIntentプロトコルに準拠する インテントを実装して 結果のタップに対応させます ターゲットパラメータは 結果と同じ エンティティ型にする必要があります OpenIntentは Image Searchの サポート以外に Spotlightなど他の場所で 呼び出されることもあり アプリ内のエンティティに移動する 簡単な方法を ユーザーに提供します

    探しているものが すぐに見つかれば 最高の体験が得られるでしょう システムUIが 完璧な候補を表示する 可能性を上げられるよう 数ページ分の結果を返します サーバを使用して より大きな データセットをクエリできます ただし 検索時間は短めにしましょう 反応が鈍い という印象を与えかねません 最後に 探しているものが 見つからなかった場合でも 検索エクスペリエンスを アプリ内で続行できるようにします TravelTrackingに追加しましょう

    必要な結果がリストにない場合 ボタンをタップすると アプリが開き 検索ビューが表示されます これを実装するには 新しいAppIntentマクロを使用して semanticContentSearch スキーマを指定します これは AssistantIntentマクロに替わる 新しいAPIで VisualIntelligenceなどの アシスタント以外の機能まで スキーマを 拡張したことで これが採用されました スキーマが要求するセマンティック コンテンツプロパティを追加します マクロはこれを自動的に インテントパラメータとしてマークします

    performメソッドで 検索メタデータを処理した後 検索ビューを表示します 追加機能として TravelTrackingに 条件に合致した観光名所の コレクションを表示させましょう ですが クエリ1つで LandmarkEntityと CollectionEntityの混在したデータを どうすれば返せるでしょうか? UnionValuesを使います まず クエリが返すエンティティの 型を表すcaseを使用して UnionValueを宣言します 次に 戻り値の型を それらの配列に変更します これで 両方の混在するデータを 返せるようになりました もちろん エンティティの型それぞれに OpenIntentの実装も 必要になります UnionValuesの詳細については KennyがApp Intentについて紹介した 2024年のセッションをご覧ください

    アプリのコンテンツを アプリ外からでも 検索できるのは 素晴らしい機能ですが Apple intelligenceを使えば アプリがアクティブの状態で できることはもっとあります 画面上のエンティティの話をしましょう NSUserActivitiesを使用すると エンティティを画面上のコンテンツに 関連付けできます これで アプリに表示されている 物事について ChatGPTに 質問できるようになります

    TravelTrackingアプリでは ナイアガラの滝を見ながら この場所が海の近くなのか Siriに尋ねることができます Siriは 私が画面上のビューを 参照していることを察知して スクリーンショットをChatGPTに 送信するよう提案します LandmarkEntityは PDFに対応しているので フルコンテンツオプションを 代わりに選択して 簡単にプレビューしてから送信できます

    応答が表示され 五大湖は 相当に大きいけれども 「海ではない」ことが分かります エンティティとビューを関連付けるには まず userActivity修飾子を LandmarkDetailViewに追加します 次に updateクロージャで エンティティ識別子を アクティビティに関連付けます 次に LandmarkEntityのデータ型を PDFなど ChatGPTが理解可能なものに 変換できるようサポートします

    そのためには Transferableプロトコルに 適合させてから PDFデータ表現を提供します 他にも プレーンテキストや リッチテキストなどに対応しています 画面上のエンティティに関する詳細は App Intentのドキュメントで紹介しています

    アプリのコンテンツを システムで 検索可能にする方法に関して Spotlightについて 少しお話しします 今はMac上で Spotlightから直接 アクションを実行できるようになりました 最高の体験を提供するため App Intentにも できることがあります

    その1. アプリエンティティを IndexedEntityに適合させて Spotlightに提供すること これにより Spotlight検索で パラメータに合わせたフィルター処理を 実行できるようになります エンティティのプロパティを Spotlightキーに関連付けるため property属性で 新しいindexingKeyパラメータが 使用可能になりました この例では 大陸に customIndexingKeyを使いました これで テキストフィールドに Asiaと入力するだけで アジアの観光名所が検索可能になります APIを採用するメリットとして ショートカットアプリが エンティティの検索アクションを 自動生成するようになります

    その2. 画面に表示された エンティティ付きコンテンツに注釈を付けて ビュー表示中は 提案において これらエンティティを優先させます その3. PredictableIntentを実装し インテントやユーザーの過去の行動から得た パラメータに基づいて システムが学習や提案を 実行できるようにします 値に応じて説明をカスタマイズして 提供することも可能です

    この機能について 詳しく知りたい場合は このセッションのショートカットと Spotlightの部分が参考になります 機能をひと通り アプリに実装できたので アプリのユーザー体験向上につながる 新しい方法を いくつかご紹介します まずはUndo機能から 途中で気が変わってもよいと分かれば ユーザーが色々試す 可能性は 高くなります それこそが アプリで実行したアクションを 元に戻せることが重要である理由です 新しいUndoableIntentプロトコルでは 馴染みのジェスチャーを使って App IntentでのUndoに 対応させることができます

    こちらはTravelTrackingアプリに 登録した コレクションの一部です 「Siriにタイプ入力」を使って カスタムショートカットの1つを実行させ 「Sweet deserts」コレクションを削除します 削除を承認すると アプリから削除されます もし ここで気が変わったら 3本の指で左にスワイプして 取り消し操作をトリガすれば コレクションを復元できます

    DeleteCollectionIntentは 最初に UndoableIntentプロトコルに準拠します このプロトコルは 取り消しアクションと 合わせて登録可能な オプションのundoManagerプロパティを 提供します setActionNameなど 他の操作も利用できます こから 関連性が最も高い 取り消しマネージャを インテントに提供します 拡張機能内で動くインテントも その対象に含まれます これにより アプリUI全体の取り消し アクションと App Intentの同期を維持し 正しい順序で 取り消しを実行できます 削除に代わるオプションを提供して インテントにさらに磨きをかけましょう

    新しい多肢選択式のAPIを使用すれば 複数のオプションを より選びやすくなります コレクションを削除するインテントを もう一度実行します 今回は ただ単に削除の確認を 求めるだけではなく アーカイブするか尋ねる オプションをプラスします

    このコレクションには 思い出がありますね 削除はせず アーカイブします performメソッドで requestChoiceを呼び出して 多肢選択スニペットを提示し betweenパラメータを使用して オプションの配列を指定します オプション名はカスタマイズできます また スタイルを指定して システムに レンダリング方法を指示できます

    ダイアログとカスタムSwiftUIビューでも スニペットをカスタマイズできます

    選択したオプションは requestChoiceメソッドから返されます ただし キャンセルオプションだけは エラーをスローして performメソッドを終了させます このエラーは検出しないようにしてください リクエストをキャンセルしたら インテントを ただちに停止させる必要があります

    フォローアップとして switchステートメントで 選択したオプションに分岐させます 作成した元のオプションは 期待値として使用できます もう1つ 手を入れる余地を紹介します サポートしているモードでは より詳細に アプリのフォアグラウンド化を制御できます これにより デバイスとのやり取りに応じて アプリの動作に変化をつけられます 例えば 運転中の インテントからの情報提供を 音声だけに制限できます 一方 デバイスを見られる状況なら 直接アプリを表示させたいところです その方が より多くの情報を 提供できるからです この機能を使えば 1つのApp Intentを 実装して 両方の状況に 対応できます その例を紹介します

    ナイアガラの滝の混雑状況を 取得できるインテントがあります

    オプションをオフにします これは ヘッドフォン装着中に Siriが使えないなど フォアグラウンド化が 不可能なシナリオに似ています 実行すると Siriにも読み上げ可能な ダイアログだけ表示されます

    一方 ダイアログをオフにし を有効にして実行すると 観光名所ページを直接開いて 混雑率や現在の天気を 確認することができます

    この機能をインテントに追加します

    まず 静的変数を使用して 対応モードを追加します バックグラウンドのみに設定すると インテントはアプリを フォアグラウンド化させません ただ 可能ならば インテントに アプリを表示させたいところです そこで インテントの実行前に アプリの起動をシステムに要求する フォアグラウンドモードを追加します

    次に 新しいcurrentModeプロパティで フォアグラウンド化されているかを 確認できるようにします 状態に応じて 操作内容が替わります でも ちょっと待ってください なぜ観光名所に 誰もいないのでしょう? なるほど 閉まっていますね このシナリオではアプリを開かないよう インテントを変更しましょう performメソッドでは 観光名所が 営業中であるかを確認して 閉まっていればすぐ終了します この場合 インテント実行前は アプリのフォアグラウンド化を させないようにします そこで フォアグラウンドモードを 動的にするよう変更します バックグラウンドモードと 3種類の フォアグラウンドモードが利用できます Immediateモードは インテントの実行前に アプリをフォアグラウンド化するよう システムに指示します Dynamicモードは アプリ起動について インテントが判断できるようにします Deferredモードは 最終的にインテントに アプリをフォアグラウンド化させますが タイミングは遅らせます GetCrowdStatusでは アプリが起動しない 場合があるため ダイナミックを選びます

    これら2つのモードでは インテントは continueInForegroundメソッドで アプリを表示させるタイミングを 正確に制御します

    観光名所が営業中と確認できたら systemContextで 新しいプロパティを使用して 表示できるかどうかを確認します continueInForeground メソッドを呼び出して アプリをフォアグラウンド化します alwaysConfirmを「false」に設定すると 数秒前にアクティビティがあった場合 システムは プロンプトを表示しません アプリの起動に成功したら その時点でようやく 混雑状況ビューを 表示できるようになります ただし システムまたはユーザーが 起動リクエストを拒否した場合 このメソッドはエラーをスローします このエラーを検出して 適宜対処することができます 最後に App Intentsを使った開発を 手助けする方法を いくつか紹介します

    App IntentでUIナビゲーションを 実行する場合は ビューを表示させる アプリステータスへのアクセスが必要です AppDependenciesやシングルトンなど グローバルに共有された オブジェクトでのみ実行できます ただし 新しいビューコントロールAPIなら UIコードをApp Intentsから削除して ビュー自体に処理を任せることができます これを利用して OpenLandmark Intentをリファクタリングしましょう まず一番に必要になるのが インテントを TargetContentProvidingIntent プロトコルに準拠させる作業です SwiftUIビューでは NavigationStackをプログラムで 変更する際に使用する パスプロパティがあります 状態としてマークされているため アクセスはビュー本体からのみ 実行できます これこそまさに onAppIntentExecutionビュー修飾子の 出番です 指定した型のインテントと そのインテントを引数とした アクションクロージャを返します。 内部では インテントの パラメータを参照して UIを適宜変更できます このコードを使えば UIコードを インテントから取り除けるほか performメソッドや 不要になった依存関係を 完全に排除できます 素晴らしいでしょう?

    システムは アプリを フォアグラウンド化する直前に アクションクロージャを実行します そのため パラメータ値の読み取り対象は インテントに絞られます 値のリクエストなど 他の操作には対応しません 同じ修飾子が 複数のビューにある場合 すべてのビューが実行されるため 各ビューで応答が可能になります アプリが複数のシーンやウインドウを サポートしている場合は 必要に応じて 特定のインテントをどれに実行させるか コントロールします 例えば TravelTrackingアプリの コレクション編集中は シーンの観光名所は 表示させないほうがよいでしょう 集中しづらいからです handlesExternalEvents APIで コントロールできるのは ありがたいですね インテント提供ターゲット コンテンツには デフォルトで contentidentifierに設定される プロパティがあります 通常は インテントに指定された名前です この値は より具体的なものになるよう いつでもカスタマイズできます

    HandlesExternalEvents 修飾子と共に シーンに使用して アクティブ化条件を設定し シーンを使用してApp Intentを実行するよう システムに指示します 既存のものがない場合は 作成されます 配列内の識別子は 処理したいIntentの コンテンツ識別子のプロパティと 一致させます ただし アクティベーション条件が 動的である場合は 代わりに 同じ修飾子を ビューに使用できます ここでは コレクション編集時以外は OpenLandmarkIntentだけを シーンに処理させています SwiftUIシーンと アクティベーション条件の詳細については これら2つのセッションで確認できます UIKitでは インテントを UISceneAppIntentプロトコルに準拠させて これらメンバーによる UISceneへの アクセスを実現できます また AppIntentSceneDelegateへの適合後 シーンデリゲートに インテント実行に応答させることもできます 最後に アクティベーション条件を指定して App Intentを処理させたいシーンを 決めることもできます 次の改善ポイントは 新しいComputedPropertyマクロです AppEntitiesへの値の保存が 回避できるようになりました SettingsEntityはUserDefaultsから defaultPlaceをコピーします 重複する値を このように構造体に 格納するのは 避けたいものです 代わりに 本当のソースから 直接導出させるのが適切でしょう 新しいComputedPropertyマクロでは ゲッタからUserDefaultsへの 直接アクセスにより これを実現できます

    また AppEntityインスタンスの 生成コストを抑えるため DeferredPropertyマクロを 新たに追加しました LandmarkEntityに含まれる crowdStatusプロパティは ネットワークサーバから値を取得する分 比較的コストがかかります システムが明示的に要求した場合だけ 取得処理を実行させたいところです

    このプロパティを DeferredPropertyとしてマークすれば 非同期ゲッタを提供できます 内部では ネットワークを呼び出す メソッドを呼び出しできます この非同期ゲッタは ショートカットなどのシステム機能が 要求した場合のみ呼び出され LandmarkEntityの作成時や 受け渡しの際には 呼び出されません

    3種類のプロパティ型の 主な違いはこうです システムオーバーヘッドが低いのでComputed Property over Deferredを選択します フォールバックは Deferredプロパティの 計算コストが高すぎる場合のみ実行します 最後になりましたが App Intentは Swiftパッケージ対応になりました

    従来は フレームワークとダイナミック ライブラリを使用して AppIntentコードの パッケージ化を実行していました 今後は Swiftパッケージと静的ライブラリに コードを入れることができます AppIntensePackageプロトコルの 詳しい使い方については 「Get to know App Intents」 セッションをご覧ください ここまで 機能の説明を お楽しみいただけたでしょうか 次のステップでは インテントのために インタラクティブスニペットが できることを紹介します 画面上のコンテンツに エンティティを関連付けて システムが自動的に エンティティを提案できるようにします 複数選択式APIを使用して ユーザーが選べるオプションを増やします 複数のモードに対応して どういう形で実行しても Intentが最適な体験を 提供できるようにします 最後に コードをさらに詳しく知るには デベロッパWebサイトの サンプルアプリをご覧ください 最新情報を皆さんと共有できて とても嬉しく思います 皆さんの創造的なアイデアを いまから心待ちにしています

    お話はここまでです ご視聴ありがとうございました

    • 4:08 - Returning a Snippet Intent

      import AppIntents
      import SwiftUI
      
      struct ClosestLandmarkIntent: AppIntent {
          static let title: LocalizedStringResource = "Find Closest Landmark"
      
          @Dependency var modelData: ModelData
      
          func perform() async throws -> some ReturnsValue<LandmarkEntity> & ShowsSnippetIntent & ProvidesDialog {
              let landmark = await self.findClosestLandmark()
      
              return .result(
                  value: landmark,
                  dialog: IntentDialog(
                      full: "The closest landmark is \(landmark.name).",
                      supporting: "\(landmark.name) is located in \(landmark.continent)."
                  ),
                  snippetIntent: LandmarkSnippetIntent(landmark: landmark)
              )
          }
      }
    • 4:31 - Building a SnippetIntent

      struct LandmarkSnippetIntent: SnippetIntent {
          static let title: LocalizedStringResource = "Landmark Snippet"
      
          @Parameter var landmark: LandmarkEntity
          @Dependency var modelData: ModelData
      
          func perform() async throws -> some IntentResult & ShowsSnippetView {
              let isFavorite = await modelData.isFavorite(landmark)
      
              return .result(
                  view: LandmarkView(landmark: landmark, isFavorite: isFavorite)
              )
          }
      }
    • 5:45 - Associate intents with buttons

      struct LandmarkView: View {
          let landmark: LandmarkEntity
          let isFavorite: Bool
      
          var body: some View {
              // ...
              Button(intent: UpdateFavoritesIntent(landmark: landmark, isFavorite: !isFavorite)) { /* ... */ }
      
              Button(intent: FindTicketsIntent(landmark: landmark)) { /* ... */ }
              // ...
          }
      }
    • 6:53 - Request confirmation snippet

      struct FindTicketsIntent: AppIntent {
      
          func perform() async throws -> some IntentResult & ShowsSnippetIntent {
              let searchRequest = await searchEngine.createRequest(landmarkEntity: landmark)
      
              // Present a snippet that allows people to change
              // the number of tickets.
              try await requestConfirmation(
                  actionName: .search,
                  snippetIntent: TicketRequestSnippetIntent(searchRequest: searchRequest)
              )
      
              // Resume searching...
          }
      }
    • 7:24 - Using Entities as parameters

      struct TicketRequestSnippetIntent: SnippetIntent {
          static let title: LocalizedStringResource = "Ticket Request Snippet"
      
          @Parameter var searchRequest: SearchRequestEntity
      
          func perform() async throws -> some IntentResult & ShowsSnippetView {
              let view = TicketRequestView(searchRequest: searchRequest)
      
              return .result(view: view)
          }
      }
    • 8:01 - Updating a snippet

      func performRequest(request: SearchRequestEntity) async throws {
          // Set to pending status...
         
          TicketResultSnippetIntent.reload()
      
          // Kick off search...
      
          TicketResultSnippetIntent.reload()
      }
    • 9:24 - Responding to Image Search

      struct LandmarkIntentValueQuery: IntentValueQuery {
      
          @Dependency var modelData: ModelData
      
          func values(for input: SemanticContentDescriptor) async throws -> [LandmarkEntity] {
              guard let pixelBuffer: CVReadOnlyPixelBuffer = input.pixelBuffer else {
                  return []
              }
      
              let landmarks = try await modelData.searchLandmarks(matching: pixelBuffer)
      
              return landmarks
          }
      }
    • 9:51 - Support opening an entity

      struct OpenLandmarkIntent: OpenIntent {
          static var title: LocalizedStringResource = "Open Landmark"
      
          @Parameter(title: "Landmark")
          var target: LandmarkEntity
      
          func perform() async throws -> some IntentResult {
              /// ...
          }
      }
    • 10:53 - Show search results in app

      @AppIntent(schema: .visualIntelligence.semanticContentSearch)
      struct ShowSearchResultsIntent {
          var semanticContent: SemanticContentDescriptor
      
          @Dependency var navigator: Navigator
      
          func perform() async throws -> some IntentResult {
              await navigator.showImageSearch(semanticContent.pixelBuffer)
      
              return .result()
          }
      
          // ...
      }
    • 11:40 - Returning multiple entity types

      @UnionValue
      enum VisualSearchResult {
          case landmark(LandmarkEntity)
          case collection(CollectionEntity)
      }a
      
      struct LandmarkIntentValueQuery: IntentValueQuery {
          func values(for input: SemanticContentDescriptor) async throws -> [VisualSearchResult] {
              // ...
          }
      }
      
      struct OpenLandmarkIntent: OpenIntent { /* ... */ }
      struct OpenCollectionIntent: OpenIntent { /* ... */ }
    • 13:00 - Associating a view with an AppEntity

      struct LandmarkDetailView: View {
      
          let landmark: LandmarkEntity
      
          var body: some View {
              Group{ /* ... */ }
              .userActivity("com.landmarks.ViewingLandmark") { activity in
                  activity.title = "Viewing \(landmark.name)"
                  activity.appEntityIdentifier = EntityIdentifier(for: landmark)
              }
          }
      }
    • 13:21 - Converting AppEntity to PDF

      import CoreTransferable
      import PDFKit
      
      extension LandmarkEntity: Transferable {
          static var transferRepresentation: some TransferRepresentation {
              DataRepresentation(exportedContentType: .pdf) {landmark in
                  // Create PDF data...
                  return data
              }
          }
      }
    • 14:05 - Associating properties with Spotlight keys

      struct LandmarkEntity: IndexedEntity {
      
          // ...
      
          @Property(indexingKey: \.displayName)
          var name: String
      
          @Property(customIndexingKey: /* ... */)
          var continent: String
      
          // ...
      }
    • 15:49 - Making intents undoable

      struct DeleteCollectionIntent: UndoableIntent {
          // ...
      
          func perform() async throws -> some IntentResult {
      
              // Confirm deletion...
      
              await undoManager?.registerUndo(withTarget: modelData) {modelData in
                  // Restore collection...
              }
              await undoManager?.setActionName("Delete \(collection.name)")
      
             // Delete collection...
          }
      }
    • 16:52 - Multiple choice

      struct DeleteCollectionIntent: UndoableIntent {
          func perform() async throws -> some IntentResult & ReturnsValue<CollectionEntity?> {
              let archive = Option(title: "Archive", style: .default)
              let delete = Option(title: "Delete", style: .destructive)
      
              let resultChoice = try await requestChoice(
                  between: [.cancel, archive, delete],
                  dialog: "Do you want to archive or delete \(collection.name)?",
                  view: collectionSnippetView(collection)
              )
      
              switch resultChoice {
              case archive: // Archive collection...
              case delete: // Delete collection...
              default: // Do nothing...
              }
          }
          // ...
      }
    • 18:47 - Supported modes

      struct GetCrowdStatusIntent: AppIntent {
      
          static let supportedModes: IntentModes = [.background, .foreground]
      
          func perform() async throws -> some ReturnsValue<Int> & ProvidesDialog {
              if systemContext.currentMode == .foreground {
                  await navigator.navigateToCrowdStatus(landmark)
              }
      
              // Retrieve status and return dialog...
          }
      }
    • 19:30 - Supported modes

      struct GetCrowdStatusIntent: AppIntent {
          static let supportedModes: IntentModes = [.background, .foreground(.dynamic)]
      
          func perform() async throws -> some ReturnsValue<Int> & ProvidesDialog {
              guard await modelData.isOpen(landmark) else { /* Exit early... */ }
      
              if systemContext.currentMode.canContinueInForeground {
                  do {
                      try await continueInForeground(alwaysConfirm: false)
                      await navigator.navigateToCrowdStatus(landmark)
                  } catch {
                      // Open app denied.
                  }
              }
      
              // Retrieve status and return dialog...
          }
      }
    • 21:30 - View Control

      extension OpenLandmarkIntent: TargetContentProvidingIntent {}
      
      struct LandmarksNavigationStack: View {
      
          @State var path: [Landmark] = []
      
          var body: some View {
              NavigationStack(path: $path) { /* ... */ }
              .onAppIntentExecution(OpenLandmarkIntent.self) { intent in
                  self.path.append(intent.landmark)
              }
          }
      }
    • 23:13 - Scene activation condition

      @main
      struct AppIntentsTravelTrackerApp: App {
          var body: some Scene {
              WindowGroup { /* ... */ }
      
              WindowGroup { /* ... */ }
              .handlesExternalEvents(matching: [
                  OpenLandmarkIntent.persistentIdentifier
              ])
          }
      }
    • 23:33 - View activation condition

      struct LandmarksNavigationStack: View {
          var body: some View {
              NavigationStack(path: $path) { /* ... */ }
              .handlesExternalEvents(
                  preferring: [],
                  allowing: !isEditing ? [OpenLandmarkIntent.persistentIdentifier] : []
              )
          }
      }
    • 24:23 - Computed property

      struct SettingsEntity: UniqueAppEntity {
      
          @ComputedProperty
          var defaultPlace: PlaceDescriptor {
              UserDefaults.standard.defaultPlace
          }
      
          init() {
          }
      }
    • 24:48 - Deferred property

      struct LandmarkEntity: IndexedEntity {
          // ...
      
          @DeferredProperty
          var crowdStatus: Int {
              get async throws {
                  await modelData.getCrowdStatus(self)
              }
          }
      
          // ...
      }
    • 25:50 - AppIntentsPackage

      // Framework or dynamic library
      public struct LandmarksKitPackage: AppIntentsPackage { }
      
      // App target
      struct LandmarksPackage: AppIntentsPackage {
          static var includedPackages: [any AppIntentsPackage.Type] {
              [LandmarksKitPackage.self]
          }
      }

Developer Footer

  • ビデオ
  • WWDC25
  • App Intentの最新テクノロジーの詳細
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習とAI
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン