How To Extend Your iOS App With SiriKit

iOS 10 introduced SiriKit, a new framework enabling apps to integrate with Siri. Roughly speaking, your apps can send messages, place calls, send payments, and more. SiriKit helps you create new experiences on iOS and explore design principles for creating a great Siri interaction.

Siri is an integral part of iOS. It has Intents.framework and contacts integration called CallKit. SiriKit support is divided into domains, each of those defines one or more tasks that can be performed. In order to use SiriKit, apps must support one of the following domains: VoIP calling, Messaging, Payments, Photos, Workouts, Ride booking, Car commands, CarPlay (automotive vendors only), and Restaurant reservations (requires additional support from Apple).

Intents and Intents UI App Extensions

SiriKit has two different types of app extensions:

  1. Required extension. Intents extension receives intent objects from the system and performs the associated tasks. Intents extension is the basis of SiriKit and supports one or more intents, runs in the background while Siri is active. Also, implements the resolve, confirm, and handle methods.
class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling {
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
}

2. Optional extension. Intents UI extension lets you customize the appearance of the Siri or Maps interface after an intent has been handled successfully. You can use an Intents UI extension if you are supporting intents in the following domains:

  • messaging
  • payments
  • ride booking
  • workouts.

Intents UI extension brings your app’s interface into Siri and provides a UIViewController Optional.

Life cycle of Intents UI extension
class IntentViewController: UIViewController, INUIHostedViewControlling
// Prepare your view controller for the interaction to handle.
func configure(with interaction: INInteraction!, context: INUIHostedViewContext, completion: ((CGSize) -> Void)!) {
// Do configuration here, including preparing views and calculating a desired size for presentation.
if let completion = completion {
completion(self.desiredSize)
}
}

Adding SiriKit

To add SiriKit to your app:

Enable Siri capability in your app.

1. In project settings, select app target, and then select the capabilities tab.

2. Add Intents extension. In Xcode navigate to Select File > New > Target.

3. Set up the name of the extension, configure the language and other options.

4. In the Info.plist file, navigate to NSExtension > NSExtensionAttributes > IntentsSupported and specify which Intents must be supported.

The IntentsRestrictedWhileLocked key (optional key) is the class name of the intent which you require the device to be unlocked for.
You can add more than one Intents extensions to your app, but each extension must support different intents. Users must grant permission for your app to use SiriKit. Also, you should add the NSSiriUsageDescription key in your iOS app’s Info.plist file.

How SiriKit Intents Work

There are 3 levels of communication between your app and Siri about Intents:

1. Resolve. Ask your app to resolve every parameter on in intent. We will call resolve few times for every parameter and confirm user request. This step helps Siri understand what user said, and more importantly, what they meant when they sent the request to your app.

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

2. Confirm. Details Siri about expected result of handling an intent. It is an opportunity for your app to check for any requirement states to complete the user request. (For example, if a user is authenticated)

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

3. Handle. Used for specific actions a user requests. You should provide as much information about the result as possible. Network calls can take time and Siri displays the waiting animation. It can provide a response within a few seconds, otherwise, you can use the InProgress response code.

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

App-specific Vocabulary

A lot of apps have unique ways of describing things and Siri needs your assistance to understand words and phrases unique to your app. Some phrases are parts of your app. You can create an app-specific vocabulary to describe words and phrases that are part of your app.

These app-specific words and phrases are defined in the plist file and can be localized. The vocabulary has 2 keys:

  • ParameterNames. The array of String. (Properties to which the specified vocabulary terms are applied).
  • ParameterVocabulary. The array of Dictionary. (The vocabulary terms, including any type of pronunciation and examples).