🧩

App Intents実践入門 - アプリのコア機能を公開する

2024/06/20に公開

本記事は、WWDC24の "Bring your app’s core features to users with App Intents" (App Intentsでアプリのコア機能をユーザーに提供する)というセッションを記事形式にしたものです [1]

https://developer.apple.com/videos/play/wwdc2024/10210/

Learn the principles of the App Intents framework, like intents, entities, and queries, and how you can harness them to expose your app’s most important functionality right where people need it most. Find out how to build deep integration between your app and the many system features built on top of App Intents, including Siri, controls and widgets, Apple Pencil, Shortcuts, the Action button, and more. Get tips on how to build your App Intents integrations efficiently to create the best experiences in every surface while still sharing code and core functionality.
(インテント、エンティティ、クエリなど、App Intentsフレームワークの原則を学び、それらを活用してアプリの最も重要な機能をユーザーが最も必要とする場所に公開する方法を学びます。Siri、コントロール、ウィジェット、Apple Pencil、ショートカット、アクションボタンなど、App Intentsの上に構築された多くのシステム機能とアプリケーションの間に深い統合を構築する方法をご覧ください。コードとコア機能を共有しながら、App Intentsの統合を効率的に構築して、あらゆるサーフェスで最高のエクスペリエンスを実現するためのヒントが得られます。)
- セッション概要より

サンプルコードと本セッションの内容

本セッションで実装を進めていくアプリのコードはこちらで公開されています。

https://developer.apple.com/documentation/AppIntents/AcceleratingAppInteractionsWithAppIntents

2
同セッション動画より引用(iOS 18で実行したスクショではありません)

このアプリの機能を、App Intentsを用いてアプリを起動していないときもいろいろな場所(e.g. Siri, ホーム画面, コントロールセンター)から使えるようにする、というのが本セッションの内容。

本セッションで作るアプリの紹介(Introductionより抜粋)

2

これは私のアプリです。サンプルコードです。開発者サイトから入手できます。トレイルのカタログです。このビデオを通して見ていきます。僕と一緒に作業してください。とにかく、これが私のアプリです。

このアプリを使っているときは、とても楽しいと言われます。その中にいない時はどうでしょう?

3

ほとんど見えない。ホーム画面にボックスがあるだけ?そうとは限らない。

4

適切なコードがあれば、私のアプリの機能をそのボックスから持ち出して、デバイスの他の場所に昇格させることができる。

6

Siriに話しかけることもできる。

9

ホーム画面にウィジェットで情報を追加することもできる。コントロールセンターにアクションやステータスを追加することもできる。だから私のアプリは、アプリの中にいなくても人々を喜ばせ続けることができる。これが、App Intentsとその機能が実現するものです。どのように機能するのですか?それを知るために、あなたは正しい場所にいるのです。

WWDC24では多くのApp Intents関連のセッションがありますが、本セッションは従来からあるAPIも含め、あらためてApp Intentsを用いてアプリのコア機能を公開(expose)する方法について基礎から解説してくれるセッション、という位置づけです。

App Intentsのコンセプトや設計の考え方について学びたいという方は下記からキャッチアップして、

https://zenn.dev/shu223/articles/design_appintents

次に実践入門編として本記事を読むと良いと思います。

本セッションのアジェンダ
  • Friction v.s. flow

アプリを使用する際のフローを改善することと、それを支援するシステム機能群についてお話しします。

  • Understanding the framework

App Intentsとは何なのか、そしてそれがこれらの機能とどのようにフィットするのか、

  • Building the code

そして、あなたのアプリのためにApp Intentsを作り始める方法。

摩擦 v.s. フロー

楽しい体験の重要な部分はフローです。心理学では、それは思考と行動の融合です。何をしようと思っても、難なく実行できる。デバイス上では、次に必要なものが常に近くにあり、簡単に手に入ることを意味する。フローを邪魔する余計なステップがあると、それは摩擦と呼ばれる。比喩的には、物事を成し遂げるのを邪魔するものを指す。
(中略)
一般的には、できる限り摩擦を減らしたいものだ。摩擦はひとつの大きな障壁である必要はない。何度もぶつかる小さな段差であることも多い。

デバイスを使うときの例として、アプリの切り替えがある。あなたのアプリは、あなたが注意深く作り上げた小さな世界、独自の箱を定義し、そのアプリの中では完璧です。しかし、人々は物事を成し遂げるために1つのアプリだけを使うわけではない。多くのアプリを使うのだ。 箱の間を移動する必要がある場合はどうなるのか?デフォルトでは、ボックスの中で何かを見たり、何かをしたりするには、そのボックスの中に入るしかない。つまり、アプリを切り替えなければならない。アプリの切り替えはそれほど難しくはないが、タダではない。アプリを切り替えるのはそれほど難しいことではないが、無料ではない。

もしデバイスがあなたの箱の中身を理解していたらどうだろう、核となる機能を理解したらどうだろう?そうすれば、あなたの箱の外、より高い位置にそれらを表示し、より素早く、より簡単に辿り着くことができるだろう。

このアイデアに基づいて、デバイスの使用体験をフロー化する、システム機能の全ファミリーがあります。

いくつかの例を見てみましょう。これらは私のアプリの例に基づいており、その地域のトレイルのカタログを、トレイルに関する様々な詳細とともに表示している。いくつかの機能は、Spotlightでの検索など、アプリ内の適切な場所に素早くたどり着くのに役立つ。ホーム画面で、ピン留めしたトレイルを見たいと思い、Spotlightを起動したとする。私が何かを入力する前に、Spotlight Suggestionsは私が例のアプリを使いたいと推測し、必要なアプリのショートカットを提供する。もしSpotlightがそれを推測しなかったとすると、私は "exa "と入力しなければならない。私が例のアプリを使いたいことは確かなので、アプリのショートカットがより目立つ位置に表示され、トップヒットとして表示される。あるいは、どこからでもSiriに言いたいことを言えたかもしれない。

アプリの切り替えが簡単になるだけでなく、そもそもアプリを切り替える必要がなくなる機能もある。例えば、お気に入りのトレイルのコンディションをチェックしたいとしよう。

例えば、トレイルのコンディションをチェックしたいとしよう。

思いつくたびにアプリを開く代わりに、ウィジェットでホーム画面に情報を追加できる。他のことをしているときに、ちらっと見ることができる。ロック画面にも追加できる、

33

そうすれば、携帯電話のロックを解除する必要さえなくなる。あるいは、コントロールセンターにカスタムコントロールを追加して、他のアプリを使いながらでもアクセスできるようにすることもできる。このような機能は他にもたくさんある、

34

そして、私たちはそれらを追加し続けています。昨年はアクションボタンを追加しました。今年はCamera Capture、Apple Pencil Pro Squeezeなどを追加します。これらはすべて、あなたのアプリの機能を箱から出して、デバイス全体の体験の一部にし、「これをやりたい」と思ってから実行するまでのフローを効率化します。

これらの機能には重要な違いがある。

35

誰が何を昇格させるかを選ぶのか?Spotlightのような機能では、開発者であるあなたが選択します。アプリ内の重要なアクションをハイライトすると、デバイスが自動的に適切な場所に表示します。あなたのアプリから起こりそうなアクションは、人々がデバイスのどこにいても常に近くにあり、フロー感覚を与えます。

37

ウィジェットやコントロールのような機能で、人々は選択する。あなたは柔軟なコンポーネントを提供し、人々は重要なものを正確に選択し、自分にとって適切なワークフローを構築することができます。

言い換えれば、人々はあなたのアプリの体験をパーソナライズすることができ、デバイスを自分自身の延長に、さらには自分自身を表現するものにすることができる。それは彼らにとっても、アプリ開発者であるあなたにとっても良いことです。なぜなら、人々があなたのアプリを自分自身や彼らの生活に合うようにカスタマイズできればできるほど、彼らがそのアプリを使い続ける可能性が高まるからだ。

フレームワークを理解する

App Intentsはそれ自体が機能というわけではありません。

42

機能を構築するための共通基盤なのです。Siri、Spotlight、ショートカットなど、これまで話してきたこれらの機能には共通のテーマがあります。

アプリ内のコア機能、アプリを使う人にとって意味のあるアクションやコンテンツを、アプリの外部に提示するのです。そのためには、システムが理解できる方法でアプリのコア機能を公開(expose)する必要があります。

App Intentsはそのためのフレームワークだ。このフレームワークは2つの大きな役割を担っている。

第一に、SiriやSpotlightのようなシステム機能が何を提示できるかを知ることができるように、コアとなるアクションとコンテンツを定義することができます。

第二に、実際に何かを提示するということは、アプリとプレゼンターの間で多くの往復コミュニケーションを意味します。例えば、誰かがSiriにあなたのアプリであることをするように指示すると、Siriはあなたのアプリにそのことをするようにメッセージを送らなければなりません。そして、あなたのアプリは、それを実行し、その結果がこれです、というメッセージを送り返す必要があります。App Intentsはそのような通信を処理するので、あなたは機能の実際の動作の実装に集中することができます。

一旦、1つのシステム機能をサポートするためにこの作業を行えば、同じ作業は様々なシステム機能で役立ちます。そのため、システム間でコードを共有することができ、まったく同じコードで複数の機能をサポートすることもできます。

次のセクションでその方法を説明します。

インテントは、ビューを開いたりハイキングを始めたりするようなアクションを実行します。アプリのコマンドや動詞だ。

47

エンティティはオブジェクトで、トレイルやコレクションのようなものです。名詞です。

インテントが動詞でエンティティが名詞だとすると、アプリのショートカットは文章です。

動詞と名詞、または名詞を埋めるための空白、その他必要なオプションが1つにまとめられ、アプリの重要な機能、つまりアプリを使う人なら誰でもやりたいと思うことを説明します。

Spotlightやアクションボタンのような機能は、その機能、その文章を提供し、人々が素早く簡単にそれを実行できるようにします。これらを組み合わせることで、あなたのアプリがデバイス上でどのように表示されるかが決まります。

コードの構築

大まかなイメージがつかめたところで、コードを書いてみましょう。

49

アプリの流れを良くするために、いくつかの機能を追加する。

50

実に5つ。

  • パラメータなしのシンプルなショートカットアクション
  • パラメータ付きのショートカットアクション
  • ホームスクリーンウィジェット
  • コントロールセンターコントロール
  • SpotlightとSiriのアプリショートカット

とても多いように聞こえるが、これらはすべてApp Intentsフレームワークに基づいており、機能間で多くのコードを共有するのに役立つ。さっそく始めよう。

Shortcuts action

Shortcutsアプリは、独自のカスタムショートカットを作成するためにアクションを探索、組み合わせ、リミックスできる強力なツールです。

54

例えば、私のチームメイトは娘とマウンテンバイクに乗るのが大好きなので、私のアプリのアクションを使って、あるエリアのサイクリングコースをランダムに選ぶカスタムショートカットを作りました。

開発者として、私はこの機能を優先しないかもしれません。しかし、私はショートカットのアクションという形で柔軟性を提供することができます。

最近、トレイルをリストの一番上にピン留めする機能を追加した。

66

そうすれば、その場所への旅行を計画しているときに、その詳細をすぐに開くことができるので、そこまでの行き方や、友人が犬を連れてくることができるかといった質問に答えることができる。

でも、今アプリに入っていなくても、すぐにそのトレイルに行けるようにしたい。

そこで、ピン留めしたトレイルの詳細をアプリで開くショートカットアクションを作ってみましょう。

完成したら、ショートカットアプリでこんな感じになる。

71

App Intentsフレームワークでは、ショートカット・アクションはインテントである。

すべてのインテントは、デフォルトでショートカットアプリのアクションとして表示されます。

インテントは、AppIntent プロトコルに準拠した型です。

76

これには2つの必要な要素がある:
アクションの名前として表示されるローカライズ可能なタイトルと、アクションを実行するperform メソッドです。

実際のサンプルコード

実際のサンプル「AppIntentsSampleApp」では、インテント名は OpenPinnedTrail ではなく、OpenFavorites となっている。

struct OpenFavorites: AppIntent {
    
    /// Every intent needs to include metadata, such as a localized title. The title of the intent is displayed throughout the system.
    static var title: LocalizedStringResource = "Open Favorite Trails"

    ...
    
    /**
     When the system runs the intent, it calls `perform()`.
     
     Intents run on an arbitrary queue. Intents that manipulate UI need to annotate `perform()` with `@MainActor`
     so that the UI operations run on the main actor.
     */
    @MainActor
    func perform() async throws -> some IntentResult {
        navigationModel.selectedCollection = trailManager.favoritesCollection
        
        /// Return an empty result, indicating that the intent is complete.
        return .result()
    }
    
    ...
}

インテントは常に、アプリを使う人にとって意味のあるアクションであるべきで、ここでは内部実装ではなく、ピン留めされたトレイルを開くことです。performメソッドは常に結果を持ちますが、このように結果が空であることもあります。

77

このインテントでは、実行時にアプリを開くようにしたい。AppIntent は、私がopenAppWhenRun でそうするように指示すれば、そのように処理してくれる。

80

これで終わりです!新しいインテントがShortcutsアプリのライブラリにアクションとして表示されます。

81

このアクションでショートカットを作れば、ショートカットアプリで実行できる。

84

さらに一歩進んで、ホーム画面に追加することもできる。

86

タップすると、アプリが起動し、ピン留めしたトレイルの詳細が表示される。

Parameterized action

これは悪くないが、開くトレイルを選べるアクションも欲しい。そうすれば、特定のトレイルを開くショートカットを作ったり、実行するときにトレイルを聞いてきたりできる。そのためには、パラメータ付きのインテントを作る必要がある。

これは最初のインテントにとても似ています。実際、最初のインテントをクローンして、名前を変えることから始めます。アプリでパラメータを開くインテントには、特別なプロトコル OpenIntent があるので、それを追加します。 [2]

91

OpenIntentopenWhenRun を暗示するので、これを削除します。

そして、プロトコルが定義するパラメータを追加する必要がある。パラメータとは、@Parameter でマークされた通常のプロパティのことで、ローカライズ可能なタイトルのような特別なメタデータを追加することができる。

ここはトレイルであるべきです。トレイルは私のアプリのコア・コンテンツ・タイプなので、 それを TrailEntity にします。

ここで覚えておくべきことが2つある。

第一に、エンティティはアプリを使う人にとって意味のあるものでなければならない。つまり、これはデータベースの行でも何でもなく、私の実装でもなく、トレイルなのだ。

第二に、エンティティを参照するパラメータはエンティティであるべきで、エンティティを記述するデータであってはならない。つまり、これはトレイルの名前でもUUIDでもなく、トレイルのエンティティなのだ。

perform メソッドを更新して、パラメータに移動させれば完了だ。TrailEntity を定義しなければならない部分を除いては。幸いなことに、それはそれほど難しいことではない。

エンティティは AppEntity プロトコルに準拠した型である。モデルの型を直接適合させることができる。これは、モデルが一度にすべてメモリに収まるほど小さく、必要なインスタンスがすでに存在する場合にうまくいく。

または、実装を参照するだけの独自の型をエンティティにすることもできます。これは、モデル・インスタンスを必要なときだけ作成する場合や、モデル・インスタンスが、あなたのインテントに必要のない高価なプロパティを持っている場合に便利です。この例では、2番目のアプローチを採用します。

したがって、TrailEntity は、基礎となるモデルタイプ "Trail "を参照することになります。

エンティティはプロパティを持つことができます。これはパラメータに非常に似ていますが、コマンドではなくコンテンツオブジェクトにあります。

エンティティは3つのものを持たなければなりません

  1. display representation、つまりデバイスがメニューに描画するようなことができるようにすること
  2. 永続的な識別子
  3. クエリ
サンプルコードでの TrailEntity の実装
struct TrailEntity: AppEntity {

    static var typeDisplayRepresentation: TypeDisplayRepresentation {
        TypeDisplayRepresentation(
            name: LocalizedStringResource("Trail", table: "AppIntents"),
            numericFormat: LocalizedStringResource("\(placeholder: .int) trails", table: "AppIntents")
        )
    }
    
    static var defaultQuery = TrailEntityQuery()

    var id: Trail.ID
  
    @Property(title: "Trail Name")
    var name: String
    
    ...
    
    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(name)",
                              subtitle: "\(regionDescription)",
                              image: DisplayRepresentation.Image(named: imageName))
    }
    
    ...
}

クエリーとは?クエリとは何かを説明する前に、クエリが何をするのかを説明しなければならない。クエリーは、エンティティを尋ねる質問を実際のエンティティに変える。そして、インテント・パラメータの値を選択すると、2つの質問をすることになる。

まず、どのようなエンティティがあるのか、そのためデバイスは、誰かが選ぶことができるオプションのリストを表示することができる。誰かが1つを選ぶと、そのエンティティのIDが保存される。

そしてインテントを実行するとき、保存されたIDを送信し、クエリは2番目の質問に答える必要がある。その答えがあれば、インテントのパラメータを埋めることができます。つまり、インテント・コードはIDだけでなく、エンティティを取得します。

そのコードはどのようなものでしょうか?クエリは、エンティティ・クエリに準拠した型です。

112

エンティティの問い合わせには、すでに述べた2つの方法だけでなく、文字列の検索、述語による検索など、さまざまな方法があります。そのため、エンティティ・クエリには、質問の種類ごとに1つずつ、いくつかのサブプロトコルがあります。

「どのようなエンティティが存在するか」という質問は、EnumerableEntityQuery で処理することができる。

115

これは、概念的に最も単純な形式のクエリである。iOS 18のSDKでビルドすれば、App Intentsはここからさらに複雑なものを導き出すことができる。もちろん、実際にすべてのエンティティを一度に返すことができる場合にのみ機能するので、モデル全体がメモリに収まる必要がある。そうでない場合、あるいは自動生成されるクエリよりも良い仕事ができる場合は、他のクエリ・プロトコルを使うべきだ。しかし、この例は十分にシンプルなので、うまくいくでしょう。

「このIDを持つエンティティは何か」という質問には、EntityQuery 自身がこのメソッド entities(for:) で答えてくれます。

117

サンプルコードでの TrailEntityQuery の実装
struct TrailEntityQuery: EntityQuery {
    
    ...
    func entities(for identifiers: [TrailEntity.ID]) async throws -> [TrailEntity] {
        ...
        
        return trailManager.trails(with: identifiers)
                .map { TrailEntity(trail: $0) }
    }
    
    ...
    func suggestedEntities() async throws -> [TrailEntity] {
        ...
        
        return trailManager.trails(with: trailManager.favoritesCollection.members)
                .map { TrailEntity(trail: $0) }
    }
}

extension TrailEntityQuery: EntityStringQuery {
    
    ...
    func entities(matching string: String) async throws -> [TrailEntity] {
        ...
        
        return trailManager.trails { trail in
            trail.name.localizedCaseInsensitiveContains(string)
        }.map { TrailEntity(trail: $0) }
    }
}

これらによって、パラメータ・コンフィギュレーションが機能するために必要なすべてが処理される。

118

これで、私のインテントを実行する準備ができた。しかし、もうひとつ追加しなければならないことがある。それはパラメータの要約だ。

これで、私のインテントはShortcutsアプリでこのように表示される。これは機能するが、開くトレイルが折り目の下にあるため、読みにくく、理解しにくい。

パラメータサマリーとは、インテントが何をするのかを説明する自然言語文のことで、すべての必須パラメータ(この場合は開くトレイル)の値を含みます。

121

これで、このインテントが何をするのかがわかりやすくなり、パラメータを編集しても読みやすくなりました。

OpenTrail を実装したコード
struct OpenTrail: OpenIntent {

    static var title: LocalizedStringResource = "Open Trail"

    @Parameter(title: "Trail")
    var target: TrailEntity

    @MainActor
    func perform() async throws -> some IntentResult {
        navigationModel.selectedTrail = trailManager.trail(with: target.id)
        return .result()
    }

    static var parameterSummary: some ParameterSummary {
        Summary("Open \(\.$target)")
    }
    ...
}

実際に見てみよう。Open Trailアクションのプレースホルダーをタップすると、

123

トレイルを選択するシートが表示されます。

125

クエリはオプションと検索を提供する。1つを選ぶと、インラインで表示されるので、このアクションが実行されたときに何をするかをすぐに読むことができます。

これがパラメータの概要です。

実は今はトレイルを選びたくない。実行時に選びたい。そこで、このパラメーターを消去する。

128

そしてホーム画面にショートカットを保存する。

130

このショートカットをタップすると、同様のピッカーが表示される。

ショートカットアクションは、アプリに柔軟性を追加する究極の方法です。人々は、あなたが想像もしなかったようなあらゆる種類のワークフローにショートカットを組み込むだろう。

Home Screen widget

情報を一目で見ることができる、より焦点を絞った柔軟性の形を見てみましょう:ウィジェットです。

トレイルに関するほとんどの詳細は、頻繁に変更されることはありません。しかし、コンディションは日常的に変わりうるし、変わるものだから、それを見張っておくには便利だろう。それはウィジェットの完璧な使い方だ。

ウィジェットは一目でわかるようにデザインされている。ピン留めしたトレイルの詳細だけを表示するウィジェットを作ることもできるが、私はいくつかの異なるトレイルを監視したい。

そのためには、ウィジェットを設定可能にして、どのトレイルを表示するかを設定できるようにする必要があります。つまり、OpenTrailアクションと同じように、ウィジェットもOpenTrailパラメータを持つことになります。

ウィジェットを作るには、ウィジェットの定義が必要です。

135

これはWidgetKitのビデオではないので、ビューやタイムラインプロバイダなどの詳細は省略します。もっと詳しく知りたい場合は、WidgetKitのドキュメントを参照してください。また、ウィジェットがApp Intentsとどのように連携するかについての詳細は、2023年からのApp Intentsの機能強化についてをご覧ください。

137

ここで重要なのは body で、特にこの WidgetConfigurationIntent タイプを設定するインテント・パラメータです。

141

設定インテントは、WidgetConfigurationIntent に適合するインテントです。面白いですね。

他のインテントと同様に、タイトルが必要です。そして、このウィジェットをトレイルで設定できるようにしたいと言ったので、パラメータを追加します。これはトレイルであるべきで、私はすでにオープンインテントに TrailEntity を定義しました。それを再利用できますか?

142

はい、できます。ウィジェットがホーム画面に追加されたときに空にできるようにオプションにしていますが、それ以外はOpenTrailインテントのパラメータと同じタイプです。

143

これで、任意のトレイルに対して設定可能なTrail Conditionsウィジェットができました。すでに書いた TrailEntity とそのクエリは、設定の値を選ぶ処理をするので、もう一度書く必要はありません。

Control Center control

驚くほど簡単だったので、もうひとつやってみよう。

146

一つはコントロールセンターコントロールで、iOS 18で新しいAPIが追加された。ウィジェットのように、このコントロールもトレイルで設定できるようにしたい。タップすると、そのトレイルの詳細がアプリで開くはずだ。

ここでは、コントロールのApp Intents関連の部分のみを取り上げる。コントロールの詳細については、"Extend your app's controls across the system "を参照してください。

それを念頭に置いて、始めよう。

実装手順

150

コントロールの基本的なアウトラインは、ControlWidget という特別なタイプのウィジェットで、コントロールの外観と動作を定義するボディを持っているということです。

151

私が目指しているコンフィギュラブル・コントロールは、コンフィギュラブル・ウィジェットと同じように、App Intentを使って設定します。

152

ボタンとして動作するはずだと言ったので、コントロールウィジェットのボタンを追加します。

これが、設定可能なボタン・コントロールの基本的な形だ。ボタンの内容を構築できるように、設定を保持するインテント型と、ボタンがタップされたときに処理するインテント・インスタンスが必要です。少し考えてみよう。コンフィギュレーションにTrailパラメータを持たせたいと言いました。さっき作った OpenTrail インテントのような感じだ。再利用できますか?もちろんできます。

162

OpenTrail について変更する必要があるのは、ControlConfigurationIntent に準拠することを宣言することです。これは extension で、インテントがまだ持っていないものを要求するわけではないので、ボディは空です。

164

これが終わったら、型を埋めることができる。

165

そして、設定された型のインスタンスを使って、ボタンの画像とテキストを作成する。そして、ここからが本当に賢いところだ。

やはりアクションが必要で、これはインテントのインスタンスで、設定されたトレイルを開く必要があります。繰り返しますが、これは OpenTrail のように聞こえます。しかし、新しいOpenTrail インスタンスを作る必要はありません。なぜなら、すでに設定インテントとして1つ持っているからです。

167

すでに必要なperformメソッドを持っていて、私が望むように設定されているからです。

動作を見てみよう。

これがコントロールセンターの新しい設定モードです。左下に新しいコントロールを追加し、モントレー湾岸トレイルを開くように設定した。

編集を止めると、コントロールがライブになるので、それをタップすると、インテントが実行され、

アプリが開いて、設定したトレイルの詳細が表示されます。

Spotlight and Siri

次に、アプリのアクションのひとつをSpotlightとSiriで自動的に利用できるようにし、ピン留めしたトレイルの詳細を開くようにします。そのためには、アプリのショートカットを作る必要がある。アプリのショートカットは、開発者であるあなたが、アプリの重要な機能としてそれを強調するインテントの周りに作成するラッパーです。Spotlight、アクションボタン、Apple Pencil Proなど、様々な機能がアプリのショートカットを提供します。

そのためのコードを見てみよう。

AppShortcut のリストである static メンバーを持つ、単一の AppShortcutsProvider を定義します。

181

AppShortcut はインテントをラップます。ここでは、先ほど作成した OpenPinnedTrail インテントをラップします。

これはインテントのインスタンスであり、 型ではないことに注意してください。つまり、ここでパラメータの一部または全部を事前に入力することができます。たくさんのパラメータを持つgeneralなインテントを、特定用途にspecificな AppShortcut としてラップすることができるのです。ショートカットの実行時に、未入力の必須パラメータがあればプロンプトが表示されるので、どちらでも機能する。どのような体験を提供したいかによる。このインテントにはパラメータがないので、カッコの中は空です。

189

アプリのショートカットはSiriから利用できるので、話すフレーズのリストがあり、それぞれにアプリ名と、Spotlight、タイトル、画像など視覚的に表示される場所が含まれていなければなりません。

実際のサンプルでの TrailShortcuts のコード

AppIntentsSampleApp では、 OpenFavorites インテント以外の他のインテントについても AppShortcut が実装されています。

class TrailShortcuts: AppShortcutsProvider {
    
    static var shortcutTileColor = ShortcutTileColor.navy
    
    static var appShortcuts: [AppShortcut] {
        ...
        
        /// `OpenFavorites` brings the app to the foreground and displays the contents of the Favorites collection in the UI.
        AppShortcut(intent: OpenFavorites(), phrases: [
            "Open Favorites in \(.applicationName)",
            "Show my favorite \(.applicationName)"
        ],
        shortTitle: "Open Favorites",
        systemImageName: "star.circle")
        
        ...
    }
}

ここで1つだけ、登録コードのようなものがないことに注目してほしい。App Intentsフレームワークは自動的にプロバイダーを検出し、登録を処理するので、私のアプリのショートカットはアプリがインストールされるとすぐに利用できる

これで、アプリのショートカットがSpotlightに表示され、Siriに話しかけることができるようになった。

SpotlightもSiriも自動で処理される。アプリをインストールする以上のことをする必要はない。

しかし、デバイスをカスタマイズしたい場合は、アクションボタンやApple Pencil Proにもアプリのショートカットが用意されている。

196

つまり、1つのコードで4つの機能が使えるのだ。また、既存のアプリのショートカットがある場合は、昨年のアクションボタンで自動的に機能したのと同じように、Apple Pencil Proでもすでに機能します。

これは良さそうだが、Siriとのインタラクションがいまいちしっくりこない。まず、ビューが表示されるので、少なくとも自分のデバイスを見ていなければならない。次に、アプリを開こうとするので、現在使っているアプリから外れてしまいます。そこで、このインテントを、代わりに情報を返す少し変わったものに置き換えてみよう。

TODO: 要整理

198

インテントを作成しますが、このインテントではピン留めされたトレイルは開きません。スニペットで表示し、ユーザーが画面を見ていない場合はそれを話します、

200

HomePodやAirPodsのようにスクリーンを持っていない場合。

まずはスタブで実行する方法から。

201

ステップ1は明らかにピン留めされたトレイルを取得している。

202

しかし、ステップ2は?アプリを使わずに情報を見せたり話したりするにはどうすればいいのだろう?私は結果を使います。performメソッドの結果は単なる値ではなく、さまざまなものの豊かな組み合わせなのだ、

203

Siriが話すダイアログや、Siriが表示するビューのスニペットなどです。

205

私の結果が「ダイアログを提供する」「スニペット・ビューを表示する」に適合していると言うなら、結果を作成するために別のメソッドを使うことができます:それはダイアログとビューを取ります。ダイアログだけ、あるいはビューだけを提供することもできる。

207

スニペットのテキストとは異なるかもしれませんが、Siriに言わせたいことを書きます。ここでは便宜上、補間された文字列を使っていますが、このパラメーターは実際にはインテント ダイアログのインスタンスです。

208

SwiftUIのビューを提供します。私のビューの正確な詳細は割愛し、それを取得するために別の関数を使用しますが、ビュービルダーの式で末尾のクロージャを使用してインラインに置くこともできました。どちらの場合でも、ビューはウィジェットのようにアーカイブされるので、Siriにリレーすることができます。なので、ウィジェットがサポートするSwiftUIの機能を使うことができます。

これで、Siriに尋ねると、

画面を見ていれば、ピン留めされたトレイルの条件をスニペットで表示してくれます。

212

Siriを解除すると、中断した場所に戻るので、戻る方法を探す必要がない。画面を見ていなければ、Siriがダイアログを話してくれる。ブーツを探すのに忙しくてスマホがポケットに入っていても、部屋の向こうにいるHomePodに状況を尋ねることができ、スマホを取り出す必要さえない。

さて、私は自分のアプリの使い方を効率化するために、とんでもない数の機能を追加した。これがApp Intentsの役割だ。コアとなるコンセプトを一度表現すれば、関連する多くの機能で再利用することができる。

Wrap-up

  • アプリを使用する際のフローを改善することができます。

    • 人々はより速く物事を成し遂げ、より楽しめるようになり、あなたのアプリの使い方を調整し、生活に組み込むことができるようになります。
  • そのためには、Siri、ショートカット、ウィジェットなど、アプリの中にいなくてもアプリを使えるような機能を採用しましょう。

  • これらの機能を採用する方法は、App Intentsを使うことです。

    • 学ぶ必要のあるAPIが少なくなり、機能間でコードを共有できるようになります。

もっと学びたいですか?もちろんです!次に見るべき素晴らしいビデオをいくつか紹介しよう。

  • あなたがまだApp Intentsの初心者なら、そして正直そうでなくても、Designing App Intentsをご覧ください。

https://zenn.dev/shu223/articles/design_appintents

  • App IntentsとSiriの連携について詳しくは、Bring Your Apps to Siriをご覧ください。

  • すでにApp Intentsをお持ちで、昨年からの新機能を知りたい方は、App Intentsの新機能をご覧ください。

ご視聴ありがとうございました。App Intentsを使った皆さんのご活躍を楽しみにしています。

脚注
  1. 個人的には動画よりも記事で読むほうがスクロールしながら読みたい部分を拾い読みできて楽なので、このような形式にしておくことにしました。 ↩︎

  2. セッションスライドでは AppIntent , OpenIntent の両方に準拠している実装になっていますが、OpenIntent 自体が AppIntent に準拠している( SystemIntent に準拠し、SystemIntentAppIntent に準拠している)ので OpenIntent のみでOKです。 ↩︎

Discussion