[WWDC18][iOS 12] Intentsを使用したSiri Shortcutsの実装方法 #WWDC18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

本記事は Apple からベータ版として公開されているドキュメントを情報源としています。 そのため、正式版と異なる情報になる可能性があります。ご留意の上、お読みください。また、本記事はNDAに配慮し、Xcode 10やiOS 12のスクショは使わず、公開情報のみで構成しています。

はじめに

WWDC18で発表された、iOS 12の新機能 "Siri Shortcuts"。 Siri Shortcutsの実装方法として、NSUserActivityを使う方法とIntentsを使用する方法があり、NSUserActivityを使用する方法については下記記事がとても参考になります。

一方で、Intentsを使った方法も気になったのでなるべく最小の構成で試してみました。

ターゲットにIntents Extensionを追加する

まずはIntents Extensionを追加しましょう。 Xcodeでプロジェクトファイルを選択し、TARGETSの左下の+ボタンをクリックし、Intents Extensionを追加します。もし、Siriとやりとりする画面にカスタムViewを表示したい場合は「Include UI Extension」にチェックをつけましょう。今回は最小の構成ということでチェックはつけませんでした。

SiriKit Intent Definition Fileを追加する

次にXcodeのFile > New > File...を選択し、テンプレートから「SiriKit Intent Definition File」を選択します。ファイル名を入力するとXcode上に.intentdefinitionファイルが追加されます。

ショートカット(Intent)を定義する

intentdefinitionファイルを開くと最初は何もない空の状態になっています。 画面左下の+ボタンをクリックして「New Intent」を選択します。

CUSTOM INTENTSに項目が追加されるので以下を定義していきます。

Custom Intent

  • Category
    • Shortcutに該当するカテゴリを選択します。例えば料理を注文するShortcutの場合は「Order」を選択するといった具合です。今回は「Do」を選択しました。
    • Categoryは以下から選択可能です。
      • Generic
        • Do
        • Run
        • Go
      • Information
        • View
        • Open
      • Order
        • Order
        • Book
        • Buy
      • Start
        • Start
        • Navigate
      • Share
        • Share
        • Post
        • Send
      • Create
        • Create
        • Add
      • Search
        • Search
        • Find
        • Filter
      • Download
        • Download
        • Get
      • Other
        • Set
        • Request
        • Toggle
        • Check In
  • Title
    • タイトル
  • Description
    • Intentの説明
  • Default Image
    • Intentのデフォルト画像。挙動を確認するのに必須ではないので今回は設定しませんでした。
  • ConfirmationのUser confirmation requiredチェックボックス
    • Shortcutを実行する前にユーザーの確認を行うかどうか。例えば料理を注文する場合などはチェックをつけて確認するべきでしょう。

Parameters

ここではIntentの処理が必要とするパラメーターを定義します。 例えば、taskNameというStringパラメーターを1つ定義しました。

Shortcut Types

  • Parametersで定義したパラメータを組み合わせてShortcutのパターンを作ります。 今回はパラメータはtaskName1つだけなのでそれを使いました。
  • また、ここで「何をするかわかりやすく簡潔な」Titleをつけます。補足事項があればSubtitleも活用します。TitleおよびSubtitleには上述で定義したパラメーターを埋めこむことができます。今回はTitleのみ「Do ${taskName}」と定義しました。
  • BackgroundのSupports background executionのチェックボックスは、バックグラウンド実行するかどうかの設定です。ここもShortcutの特性に合わせて設定しましょう。

Responseを定義する

次にResponse、つまり、Shortcutを実行した結果の形式について定義します。

  • Properties
    • Parametersと同じようにプロパティの名前と型を定義できます。ここで定義したプロパティは後述するResponse Templates内で使用することができます。
    • 今回はParametersと同様taskNameというStringプロパティを1つ定義しました。
  • Response Templates
    • Siricutの実行結果をユーザーに伝える際のメッセージテンプレートを定義できます。Codeはあらかじめ失敗(failure)と成功(success)が定義されていますが必要なら独自のCodeを追加できます。
    • 今回はCodeがsuccessのTemplateに定義したプロパティを使い、I've finished ${taskName}と定義しました。

intentdefinitionファイルからコードが自動生成される

intentdefinitionファイルにIntentやそのResponseを定義してビルドすると、対応するクラスが自動生成されます。

ショートカットを提供する

ショートカットが定義できたら自動生成したクラスを使ってショートカットをシステムに対して提供するコードを記述します。

下記サンプルの場合、DoSomethingIntentがintentdefinitionファイルから自動生成したIntentを表すクラスです。やっていることはシンプルで、Intentを作成して必要情報をセットしたらそこからINInteractionを作成してdonateするだけです。

let intent = DoSomethingIntent()
intent.taskName = "a certain task"
let interaction = INInteraction(intent: intent, response: nil)
interaction.donate { error in
    // エラーハンドリング
}

ショートカットをハンドルする

ショートカットをハンドルするコードを記述します。 INExtensionを継承したクラスでhandler(for:)メソッドをオーバーライドします。 ここではインテントの種類に応じて適切なハンドラーオブジェクトを返す処理をしています。

class IntentHandler: INExtension {

    override func handler(for intent: INIntent) -> Any? {
        switch intent {
        case is DoSomethingIntent:
            return DoSomethingIntentHandler()
        default:
            return nil
        }
    }
}

次に実際にインテントをハンドルするクラスを実装します。handle(intent:completion:)メソッドで任意の処理を行い、結果をcompletionハンドラーで実行しています。DoSomethingIntentHandlingも自動生成したプロトコルです。当該プロトコルにはhandle(intent:completion:)メソッドの他にconfirm(intent:completion:)メソッドが定義されていましたが、こちらの実装は任意でした。このメソッドはhandle(intent:completion:)メソッドの前に実行され、処理に必要な前提条件が揃っているかを検証するためのメソッドです。

class DoSomethingIntentHandler: NSObject, DoSomethingIntentHandling {

    func handle(intent: DoSomethingIntent, completion: @escaping (DoSomethingIntentResponse) -> Void) {
        guard let taskName = intent.taskName else {
            completion(DoSomethingIntentResponse(code: .failure,
                                                 userActivity: nil))
            return
        }

        // ここで何か処理を行う

        let response = DoSomethingIntentResponse.success(taskName: taskName)
        completion(response)
    }
}

実行結果

アプリを実行し、ショートカットを提供する処理が実行されると、設定アプリのSiriと検索のショートカット一覧に該当のショートカットが表示されます。ここからショートカットを実行する自分用のフレーズを登録できます。

Siriの言語で英語と日本語を試してみたところ、英語の方は問題なくintentdefinitionファイルに定義したResponseメッセージが返ってくるのですが、日本語の場合はメッセージが返ってきませんでした。(Siriの言語とResponseメッセージは同じ言語に揃えた状態) ただし、デバッグしてみたところ、日本語の場合はメッセージが返ってこないだけでショートカットの処理自体は動いていました。このあたりの挙動はベータだからの可能性がありそうです。

おわりに

Intentsを使用したSiri Shortcutsの実装方法をご紹介しました。今回はCustom Intentを作成したましたが、既存のIntent(System Intent)をカスタマイズすることもできますので試してみてはいかがでしょうか(^^)

参考情報