【问题标题】:Siri Integration: Audio call using Siri?Siri 集成:使用 Siri 进行音频通话?
【发布时间】:2016-12-29 13:25:21
【问题描述】:

我需要集成 Siri 才能使用我的应用拨打电话。 Siri 在设置的应用支持上识别我的应用。但是当我试图打电话给一些联系人说“在 MyApp 上呼叫 ContactName”时,它只会显示“打开 MyApp”按钮。 IntentHandler.swift 包含这个函数:

class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling, INStartAudioCallIntentHandling {

    override func handler(for intent: INIntent) -> Any {
        // This is the default implementation.  If you want different objects to handle different intents,
        // you can override this and return the handler you want for that particular intent.

        return self
    }

    func handle(startAudioCall intent: INStartAudioCallIntent, completion: @escaping (INStartAudioCallIntentResponse) -> Swift.Void) {
        let response: INStartAudioCallIntentResponse
        defer {
            completion(response)
        }

        let contacts = (intent.contacts ?? []).filter({ (contact) -> Bool in
            return contact.personHandle?.type == .phoneNumber && contact.personHandle?.value != nil
        })
        // Ensure there is a contact and a handle
        guard contacts.count > 0 else {
            response = INStartAudioCallIntentResponse(code: .failure, userActivity: nil)
            return
        }

        let userActivity = NSUserActivity(activityType: "INStartAudioCallIntent")

        response = INStartAudioCallIntentResponse(code: .continueInApp, userActivity: userActivity)
        completion(response)

    }

    func resolveContacts(forStartAudioCall intent: INStartAudioCallIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
        var resolutionResults = [INPersonResolutionResult]()

        if let recipients = intent.contacts {
            if recipients.count == 0 {
                let response = [INPersonResolutionResult.needsValue()]
                completion(response)
                return
            } else if recipients.count == 1 {
                if let recipient = recipients.first {
                    if self.containContact(displayName: (recipient.displayName)) {
                        resolutionResults.append(INPersonResolutionResult.success(with: recipient))
                    } else {
                        resolutionResults.append(INPersonResolutionResult.unsupported())
                    }
                }
            } else if recipients.count > 1 {
                resolutionResults.append(INPersonResolutionResult.disambiguation(with: recipients))

            } else {
                resolutionResults.append(INPersonResolutionResult.unsupported())
            }
        }
        completion(resolutionResults)
    }
    func confirm(startAudioCall intent: INStartAudioCallIntent, completion: @escaping (INStartAudioCallIntentResponse) -> Swift.Void) {
        let userActivity = NSUserActivity(activityType: "INStartAudioCallIntent")
        let response = INStartAudioCallIntentResponse(code: .ready, userActivity: userActivity)
        completion(response)

    }

    // MARK: - INSendMessageIntentHandling

    // Implement resolution methods to provide additional information about your intent (optional).


    func resolveRecipients(forSendMessage intent: INSendMessageIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
        if let recipients = intent.recipients {

            // If no recipients were provided we'll need to prompt for a value.
            if recipients.count == 0 {
                completion([INPersonResolutionResult.needsValue()])
                return
            }

            var resolutionResults = [INPersonResolutionResult]()
            for recipient in recipients {
                let matchingContacts = [recipient] // Implement your contact matching logic here to create an array of matching contacts
                switch matchingContacts.count {
                case 2  ... Int.max:
                    // We need Siri's help to ask user to pick one from the matches.
                    resolutionResults += [INPersonResolutionResult.disambiguation(with: matchingContacts)]

                case 1:
                    // We have exactly one matching contact
                    resolutionResults += [INPersonResolutionResult.success(with: recipient)]

                case 0:
                    // We have no contacts matching the description provided
                    resolutionResults += [INPersonResolutionResult.unsupported()]

                default:
                    break

                }
            }
            completion(resolutionResults)
        }
    }

    func resolveContent(forSendMessage intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
        if let text = intent.content, !text.isEmpty {
            completion(INStringResolutionResult.success(with: text))
        } else {
            completion(INStringResolutionResult.needsValue())
        }
    }

    // Once resolution is completed, perform validation on the intent and provide confirmation (optional).

    func confirm(sendMessage intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
        // Verify user is authenticated and your app is ready to send a message.

        let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
        let response = INSendMessageIntentResponse(code: .ready, userActivity: userActivity)
        completion(response)
    }

    // Handle the completed intent (required).

    func handle(sendMessage intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
        // Implement your application logic to send a message here.

        let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
        let response = INSendMessageIntentResponse(code: .success, userActivity: userActivity)
        completion(response)
    }

    // 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)
    }

    // MARK: - INSetMessageAttributeIntentHandling

    func handle(setMessageAttribute intent: INSetMessageAttributeIntent, completion: @escaping (INSetMessageAttributeIntentResponse) -> Void) {
        // Implement your application logic to set the message attribute here.

        let userActivity = NSUserActivity(activityType: NSStringFromClass(INSetMessageAttributeIntent.self))
        let response = INSetMessageAttributeIntentResponse(code: .success, userActivity: userActivity)
        completion(response)
    }

    func containContact(displayName: String) -> Bool {
        //fetch contacts and check, if exist retun YES else NO
        return true
    }
}
}

        let contacts = (intent.contacts ?? []).filter({ (contact) -> Bool in
            return contact.personHandle?.type == .phoneNumber && contact.personHandle?.value != nil
        })
        // Ensure there is a contact and a handle
        guard contacts.count > 0 else {
            response = INStartAudioCallIntentResponse(code: .failure, userActivity: nil)
            return
        }

        let userActivity = NSUserActivity(activityType: "INStartAudioCallIntent")

        response = INStartAudioCallIntentResponse(code: .continueInApp, userActivity: userActivity)

    }

我做错了什么?我错过了什么?

【问题讨论】:

    标签: ios ios10 siri sirikit


    【解决方案1】:

    请检查以下内容:

    在 Extension 的目标中为 IntentsSupported 设置 INStartAudioCallIntent。

    在默认提供的IntentHandler类中确认INStartAudioCallIntentHandling协议,实现如下方法:

    -(void)resolveContactsForStartAudioCall:(INStartAudioCallIntent *)intent
                              withCompletion:(void (^)(NSArray<INPersonResolutionResult *> *resolutionResults))completion{
        NSArray<INPerson *> *recipients = intent.contacts;
        NSMutableArray<INPersonResolutionResult *> *resolutionResults = [NSMutableArray array];
        if (recipients.count == 0) {
            completion(@[[INPersonResolutionResult needsValue]]);
            return;
        }else if(recipients.count==1){
            if ([self contactExist:recipients.firstObject.displayName]) {// check if person contains in contact or not
                [resolutionResults addObject:[INPersonResolutionResult successWithResolvedPerson:recipients.firstObject]];
            }else [resolutionResults addObject:[INPersonResolutionResult unsupported]];
        }else if(recipients.count>1){
            [resolutionResults addObject:[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:recipients]];
        }else{
            [resolutionResults addObject:[INPersonResolutionResult unsupported]];
        }
        completion(resolutionResults);
    }
    
    -(void)confirmStartAudioCall:(INStartAudioCallIntent *)intent
                       completion:(void (^)(INStartAudioCallIntentResponse *response))completion{
        NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INStartAudioCallIntent class])];
        INStartAudioCallIntentResponse *response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeReady userActivity:userActivity];
        completion(response);
    }
    
    -(void)handleStartAudioCall:(INStartAudioCallIntent *)intent
                      completion:(void (^)(INStartAudioCallIntentResponse *response))completion{
        NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INStartAudioCallIntent class])];
        INStartAudioCallIntentResponse *response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeContinueInApp userActivity:userActivity];
        completion(response);
    }
    

    您可能需要与扩展共享主要应用的联系人,使用应用分组。关注apple document了解更多信息。

    确保扩展目标支持您的设备操作系统。

    【讨论】:

    • 感谢您的回复!我没有确切地知道如何获取联系人?如何获取 IntentHandler 的应用程序联系人列表并获取它?你能帮帮我吗?
    • 我将上面的代码添加到我的 IntentHandler 并添加了 INStartAudioCallIntent。但是 Siri 仍然说我的应用不支持它。
    • 首先请检查您的目标是否有 siri 扩展和设备 iOS,如第二张图片。关于您的第一条评论,在我的情况下,我将我的应用程序联系人存储到组目录中(不在应用程序的 doc 目录中),然后从 siri 扩展中获取它们。或者您可以直接从您的预期处理程序加载它们。如果您遵循应用程序分组,您可以为应用程序和 siri 扩展配置应用程序 ID。查看苹果文档。这是一个教程:atomicbird.com/blog/sharing-with-app-extensions
    • 我添加了目标,设备的iOS是10.2。当我说:在 MyApp 上呼叫联系人时,Siri 说:我希望我可以,但 MyApp 尚未与我设置。似乎 siri 无法识别呼叫意图......但我看到我的应用程序支持 Siri 应用程序......不明白我的错误是什么。
    • 请在您的回答中分享更多来自 IntendHandler 的代码。还有一件事,当您添加 Siri 扩展时,它提供了消息意图的默认代码。所以检查它是否有效 - 使用 发送消息。我不确定,但这应该可以工作,请通过设置断点检查resolveContacts。 developer.apple.com/reference/intents/… 点击此链接了解扩展调试:easynativeextensions.com/how-to-debug-your-ios-extension
    猜你喜欢
    • 2017-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-26
    • 1970-01-01
    相关资源
    最近更新 更多