あたも技術ブログ

セットジャパンコーポレーションの社員が運営しています。

【iOS 10】SiriKit触ってみた

さて、iOS 10から追加されたSiriKitを使うことで、Siriからアプリの機能を使うことができるようになります。 今回はSiriからの機能を使い、メッセージを送るためのサンプルを紹介します。

公式のサンプルや執筆時点でネット上に存在する情報を元に書いているので、動作に問題があるかもしれませんが、とりあえずSiriからの呼び出しが成功したので、手順を紹介します。

環境

Xcode Version 8.0 beta 6

iOS 10 beta 8

プロジェクト設定

まず、アプリに対して、Siriの使用を有効にします。 プロジェクトファイルを開き、[Target]からアプリ本体を選択します。 Capabilityタブを開き、SiriをONにして下さい。

f:id:atamo_dev:20160908032005p:plain

次に、Siriから呼ばれるハンドラを作成します。 [File]→[New]→[Target]を選択

f:id:atamo_dev:20160908031956p:plain

iOSタブから[Intents Extension]を選択します。 任意の名前をつけ、プロジェクトにターゲットを追加します。

f:id:atamo_dev:20160908032045p:plain

次に、追加したターゲットのinfo.pristファイルを開き、[NSExtension]→[NSExtensionAttributes]→[IntentsSupported]の中身をINSendMessageIntentのみにします。

f:id:atamo_dev:20160908032207p:plain

コード

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自身へのメッセージと勘違いをされ、「はい、なんでしょう?」のように応答されたので、混同しにくい適当な名前にしましょう。

今回は「はなこ」というアプリ名にしました。

f:id:atamo_dev:20160908032459j:plain

f:id:atamo_dev:20160908032506j:plain