Siri 集成:使用 Siri 进行音频通话?
Siri Integration: Audio call using Siri?
我需要集成 Siri 才能使用我的应用拨打电话。 Siri 在设置的应用程序支持上识别我的应用程序。但是当我试图打电话给某个联系人说 "call ContactName on MyApp" 时,它只显示按钮 "open 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)
}
我做错了什么?我错过了什么?
请检查以下内容:
在扩展的目标中为 IntentsSupported 设置 INStartAudioCallIntent。
确认默认提供的IntentHandlerclass中的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获取更多信息。
确保扩展目标支持您的设备OS。
我需要集成 Siri 才能使用我的应用拨打电话。 Siri 在设置的应用程序支持上识别我的应用程序。但是当我试图打电话给某个联系人说 "call ContactName on MyApp" 时,它只显示按钮 "open 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)
}
我做错了什么?我错过了什么?
请检查以下内容:
在扩展的目标中为 IntentsSupported 设置 INStartAudioCallIntent。
确认默认提供的IntentHandlerclass中的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获取更多信息。