【Swift/iOS】App Intentsの実装方法!ショートカットやSiriの実装

【Swift/iOS】App Intentsの実装方法!ショートカットやSiriの実装

この記事からわかること

  • Swift/iOSApp Intents実装方法
  • ショートカットSiri操作実行するには?
  • IntentResultプロトコルApp 使い方

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

公式リファレンス:App Intentでアプリのコア機能をユーザーに提供 - WWDC2024

App Intentsフレームワーク

公式リファレンス:App Intentsフレームワーク

App Intentsフレームワーク」はアプリがSiriやショートカット、ウィジェットと連携するための機能を提供しているフレームワークです。アプリで提供している機能やデータに対してユーザーが音声コマンドやショートカットを経由してアクセスできるようになります。

実際にコードベースで見るとSiriやショートカットと連携するためには「App Intent」という特定のインターフェースを定義する必要があります。

App Intent

App Intentはアプリで実行可能な動作を示すクラスでショートカットやSiriなどのシステム機能を通じて外部からアプリの機能を利用できるようにします。AppIntentプロトコルが用意されており、これを継承してクラスを定義しておくことで反映されるようになります。

struct AppTestIntent: AppIntent {
    /// インテントのタイトル
    static var title = LocalizedStringResource("インテントタイトル")
    /// インテントの説明(未定義でもOK)
    static var description = "インテントの説明だよ"
    /// インテント実行時にアプリを起動させるかどうか(未定義でもOK/未定義の場合はfalseと同義)
    static var openAppWhenRun: Bool = true
    
    /// インテント実行時に実行したい処理
    func perform() async throws -> some IntentResult {
        return .result()
    }
}

AppIntentプロトコルを継承させて必須になるのはインテントのタイトルを定義するtitleプロパティとインテント実行時に実行したい処理を定義するperformメソッドです。

ショートカットアプリに表示

AppIntentプロトコルを継承したクラスは定義するだけで勝手にショートカットアプリに表示されるようになります。ショートカットアプリを起動して右上の「 + 」からアクションを追加する際にアプリ一覧に対象のアプリ(例:TestApp)が表示されるようになります。

【Swift/iOS】App Intentsの実装方法!ショートカットやSiriの実装

アプリを選択すると定義されているインテントが表示されます。titleに指定した部分が一覧で表示され、descriptionで指定した値は「」をクリックすると表示されます。このインテントを追加して実行してみると今回の場合は処理は空でopenAppWhenRunにtrueを指定しているのでアプリのみ起動します。

【Swift/iOS】App Intentsの実装方法!ショートカットやSiriの実装

Siriでの呼び出し

ショートカットに追加したインテントはSiriでも呼び出しが可能になります。例えば以下の場合は「Hey Siri インテントタイトル」で反応してインテントを実行してくれるようになります。タイトル部分に指定した文言で呼び出せるようになるみたいですね。

【Swift/iOS】App Intentsの実装方法!ショートカットやSiriの実装

インテントで実行する処理を定義する:IntentResultプロトコル

公式リファレンス:IntentResultプロトコル

インテントで実行する処理の定義performメソッドで行うことを説明しました。先ほどの実装を見てみると返却する値は見慣れないsome IntentResultという型になっています。some型を隠蔽するため付与されておりここでは「具体的な型は非公開だがIntentResultプロトコルに準拠した何らかの型」であることを表します。

/// インテント実行時に実行したい処理
func perform() async throws -> some IntentResult {
    return .result()
}

resultメソッドがインテント実行完了後の動作を定義する部分になります。これにはいろいろな引数がありそれによって処理を変更することが可能です。

結果を返す:.result(value:)

実行した際に何かしらの結果を返したい場合は.result(value:)を使用します。値を返す場合は返り値の型をsome IntentResult & ReturnsValue<返却する型>に変更します。

struct ReturnResultIntent: AppIntent {
    static var title = LocalizedStringResource("Return Result")
    
    /// 返り値の型を変更する
    /// some IntentResultのままでもビルドできてしまうが実行時にエラーになる
    /// Thread 5: Fatal error: perform() returned types not declared in method signature
    func perform() async throws -> some IntentResult & ReturnsValue<String> {
        if Int.random(in: 0 ... 1) == 1 {
            return .result(value: "成功")
        } else {
            return .result(value: "失敗")
        }
    }
}

ここで返却した値はショートカットで何かしらの処理の流れを実装する際に後続の処理に渡すことが可能になります。例えば「成功が返ればTestFlightアプリを起動する」なら以下のようになります。

【Swift/iOS】App Intentsの実装方法!ショートカットやSiriの実装

結果をダイアログで表示する:.result(value:, dialog:)

インテントの実行完了時に何かしらのメッセージをダイアログでユーザーに表示したい場合は.result(value:, dialog:)を使用します。返り値にはさらにProvidesDialogを追加します。

struct ReturnResultDialogIntent: AppIntent {
    static var title = LocalizedStringResource("Return Result Dialog")
    
    /// IntentDialogの引数に表示したいメッセージを渡す
    func perform() async throws -> some IntentResult & ReturnsValue<String> & ProvidesDialog {
        if Int.random(in: 0 ... 1) == 1 {
            return .result(value: "成功", dialog: IntentDialog("成功"))
        } else {
            return .result(value: "失敗", dialog: IntentDialog("失敗"))
        }
    }
}

実行すると以下のようなダイアログが表示されます。もちろん結果も返却しているので後続の処理に渡すことも可能です。

【Swift/iOS】App Intentsの実装方法!ショートカットやSiriの実装

カスタムダイアログで表示する:.result(value:, dialog:, view:)

カスタムダイアログでユーザーに表示したい場合は.result(value:, dialog:, view:)を使用します。返り値にはさらにShowsSnippetViewを追加します。

struct ReturnResultCustomDialogIntent: AppIntent {
    static let title: LocalizedStringResource = "Return Result Custom Dialog"

    func perform() async throws -> some IntentResult & ReturnsValue<String> & ProvidesDialog & ShowsSnippetView {
        let result = "成功"
        return .result(
            value: result,
            dialog: "カスタムダイアログタイトル",
            view: CustomDialogView(result: result))
    }
    
    private func CustomDialogView(result: String) -> some View {
        HStack {
            Text("成功")
                .padding()
                .frame(width: 100)
                .foregroundStyle(.white)
                .background(result == "成功" ? Color.green : Color.gray)
                .clipShape(RoundedRectangle(cornerRadius: 20))
            
            Text("失敗")
                .padding()
                .frame(width: 100)
                .foregroundStyle(.white)
                .background(result == "失敗" ? Color.green : Color.gray)
                .clipShape(RoundedRectangle(cornerRadius: 20))
        }
    }
}
【Swift/iOS】App Intentsの実装方法!ショートカットやSiriの実装

アプリ内に保持しているデータを返却する

定義したインテントの中からアプリ内に保持しているデータにも問題なくアクセスできるようです。

struct FetchDataDialogIntent: AppIntent {
    static var title = LocalizedStringResource("Fetch Data Dialog")
    
    func perform() async throws -> some IntentResult & ReturnsValue<String> & ProvidesDialog {
        // UserDefaultsからデータを取得する
        let result = UserDefaultsRepository.sheard.getStringData(key: UserDefaultsKey.KEY_DATA)
        return .result(value: "保存しているデータ", dialog: IntentDialog(stringLiteral: result))
    }
}

ショートカットにデフォルトで登録する

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index