【iOS 10】SiriKit触ってみた
さて、iOS 10から追加されたSiriKitを使うことで、Siriからアプリの機能を使うことができるようになります。 今回はSiriからの機能を使い、メッセージを送るためのサンプルを紹介します。
公式のサンプルや執筆時点でネット上に存在する情報を元に書いているので、動作に問題があるかもしれませんが、とりあえずSiriからの呼び出しが成功したので、手順を紹介します。
環境
・Xcode Version 8.0 beta 6
・iOS 10 beta 8
プロジェクト設定
まず、アプリに対して、Siriの使用を有効にします。 プロジェクトファイルを開き、[Target]からアプリ本体を選択します。 Capabilityタブを開き、SiriをONにして下さい。
次に、Siriから呼ばれるハンドラを作成します。 [File]→[New]→[Target]を選択
iOSタブから[Intents Extension]を選択します。 任意の名前をつけ、プロジェクトにターゲットを追加します。
次に、追加したターゲットのinfo.pristファイルを開き、[NSExtension]→[NSExtensionAttributes]→[IntentsSupported]の中身をINSendMessageIntentのみにします。
コード
Intents ExtensionのIntentHandler.swiftを編集します。
import Intents class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling { override func handler(for intent: INIntent) -> Any { return self } // MARK:- 解決フェーズ // 宛先の選択時にコールされる func resolveRecipients(forSendMessage intent: INSendMessageIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) { NSLog("resolveRecipients : %@", intent.recipients?.description ?? "nil") guard let recipients = intent.recipients , recipients.count > 0 else { completion([INPersonResolutionResult.needsValue()]) return } let results:[INPersonResolutionResult] = recipients.flatMap{ INPersonResolutionResult.success(with: $0)} completion(results) } // 送信テキストの入力時にコールされる func resolveContent(forSendMessage intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) { NSLog("resolveContent : %@", intent.content ?? "nil") // メッセージ内容は必須なので、指定されていない場合は、needsValue()を設定 let result = intent.content != nil ? INStringResolutionResult.success(with: intent.content!) : INStringResolutionResult.needsValue() completion(result) } // MARK:- 確認フェーズ // 送信の確認時にコールされる func confirm(sendMessage intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) { NSLog("confirm : %@", intent.description) let response = INSendMessageIntentResponse(code: .success, userActivity: nil) completion(response) } // 送信の実行時にコールされる func handle(sendMessage intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) { // Implement your application logic to send a message here. NSLog("sendMessage : %@", intent.content ?? "nil"); let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self)) let response = INSendMessageIntentResponse(code: .success, userActivity: userActivity) completion(response) } // MARK:- Handling Intentフェーズ func handle(setMessageAttribute intent: INSetMessageAttributeIntent, completion: @escaping (INSetMessageAttributeIntentResponse) -> Void) { NSLog("handle %@", intent.description) // メッセージ送信処理を追加 let response = INSetMessageAttributeIntentResponse(code: .success, userActivity: nil) completion(response) } func resolveGroupName(forSendMessage intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Swift.Void){ NSLog("resolveGroupName %@", intent.groupName ?? "nil") // グループに対して送信する場合 let notRequired = INStringResolutionResult.notRequired() completion(notRequired) } func resolveSenders(forSearchForMessages intent: INSearchForMessagesIntent, with completion: @escaping ([INPersonResolutionResult]) -> Swift.Void){ NSLog("resolveSender %@", intent.senders?[0].displayName ?? "nil") let person = INPerson(personHandle: INPersonHandle(value: "Value", type: .emailAddress), nameComponents: PersonNameComponents(), displayName: "displayName", image: nil, contactIdentifier: nil, customIdentifier: nil) let sender = intent.senders == nil ? INPersonResolutionResult.success(with: person) : INPersonResolutionResult.success(with: intent.senders![0]) completion([sender]) } func resolveServiceName(forSendMessage intent: INSendMessageIntent, with completion: (INStringResolutionResult) -> Void) { NSLog("resolveServiceName %@", intent.description) let notRequired = INStringResolutionResult.notRequired() completion(notRequired) } // Implement handlers for each intent you wish to handle. As an example for messages, you may wish to also handle searchForMessages and setMessageAttributes. // MARK: - INSearchForMessagesIntentHandling func handle(searchForMessages intent: INSearchForMessagesIntent, completion: @escaping (INSearchForMessagesIntentResponse) -> Void) { // Implement your application logic to find a message that matches the information in the intent. let userActivity = NSUserActivity(activityType: NSStringFromClass(INSearchForMessagesIntent.self)) let response = INSearchForMessagesIntentResponse(code: .success, userActivity: userActivity) // Initialize with found message's attributes response.messages = [INMessage( identifier: "identifier", content: "I am so excited about SiriKit!", dateSent: Date(), sender: INPerson(personHandle: INPersonHandle(value: "sarah@example.com", type: .emailAddress), nameComponents: nil, displayName: "Sarah", image: nil, contactIdentifier: nil, customIdentifier: nil), recipients: [INPerson(personHandle: INPersonHandle(value: "+1-415-555-5555", type: .phoneNumber), nameComponents: nil, displayName: "John", image: nil, contactIdentifier: nil, customIdentifier: nil)] )] completion(response) } }
実行
実際にSiriから呼び出してみましょう。
「アプリ名 + メッセージを送って」と言うと呼び出してくれます。
はじめは Siri Test というアプリ名を設定していたのですが、 Siri自身へのメッセージと勘違いをされ、「はい、なんでしょう?」のように応答されたので、混同しにくい適当な名前にしましょう。
今回は「はなこ」というアプリ名にしました。