Guides and Sample Code

Developer

SiriKit Programming Guide

On This Page

Resolving and Handling Intents

The entry point of your Intents extension is the INExtension object, whose sole job is to direct Siri to the objects capable of handling incoming intents. In your extension, there are three types of objects that you use regularly to handle intents:

  • An intent object contains the data that Siri gathered from the user.

  • A handler object is a custom object that you use to resolve, confirm, and handle the intent.

  • A response object contains your response to the intent.

When SiriKit has an intent object for your app to handle, it asks your INExtension object to provide a handler object capable of handling the intent. Handler objects adopt specific protocols for the intents they handle. The handler protocols are all structured identically, with methods for resolving, confirming, and handling the specified intent.

When implementing a handler, you must always implement the method for handling the intent. All other methods are optional, but recommended. Use the methods for resolving and confirming an intent to validate the contents of that intent before you try to handle it. During the validation process, Siri interacts with the user as needed to gather additional information that you require.

Figure 3-1 illustrates the high-level flow between SiriKit and one of your handler objects. When the user makes a request involving your app, SiriKit creates a corresponding intent object. If the intent has parameters that need to be resolved, SiriKit asks your handler to resolve those parameters individually. When all parameters have been resolved, SiriKit asks you to confirm your readiness to handle the intent. If you indicate that you are ready, SiriKit tells you to handle the intent. At that point, you perform the task represented by the intent.

Figure 3-1Handling a ride request intent image: ../Art/resolve_confirm_handle_cycle_2x.png

For a list of classes and protocols used to implement intent, response, and handler objects, see Intents Domains.

Dispatching Intents to Handler Objects

When an intent arrives for your app, SiriKit asks your Intents extension to provide a handler object for that intent. Specifically, SiriKit calls the handlerForIntent: method of your INExtension subclass, which is the entry point for your extension. From this method, you return the objects that SiriKit uses to resolve, confirm, and handle the intent.

Listing 3-1 shows an implementation of a handlerForIntent: method that provides handler objects for intents from the messages and VoIP calling domains. The structure of this method is always the same, regardless of which intents you handle. You check the class of the incoming intent and return a handler object that is able to handle that intent. You can use a single handler object for all of your supported intents or create different handlers for each intent. In the example, the Intents extension returns the same handler object for all of the message-related intents and different handlers for audio and video calling intents.

Listing 3-1Returning the handler for an intent

Objective-C

  1. - (nullable id) handlerForIntent:(INIntent*)intent {
  2. id handler = nil;
  3. // You can substitute other objects based on the specific intent
  4. if ([intent isKindOfClass:[INSendMessageIntent class]] ||
  5. [intent isKindOfClass:[INSearchForMessagesIntent class]] ||
  6. [intent isKindOfClass:[INSetMessageAttributeIntent class]]) {
  7. handler = [[MyMessageHandler alloc] init];
  8. }
  9. else if ([intent isKindOfClass:[INStartAudioCallIntent class]]) {
  10. handler = [[MyAudioCallHandler alloc] init];
  11. }
  12. else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) {
  13. handler = [[MyVideoCallHandler alloc] init];
  14. }
  15. return handler;
  16. }

Swift

  1. func resolveDropOffLocation(forListRideOptions intent: INListRideOptionsIntent, with completion: @escaping (INPlacemarkResolutionResult) -> Void) {
  2. var result: INPlacemarkResolutionResult
  3. if let location = intent.dropOffLocation {
  4. // If the location is valid, use it; otherwise,
  5. // let the user know it is not supported
  6. if self.locationIsInsideServiceArea(location) {
  7. result = INPlacemarkResolutionResult.success(with: location)
  8. }
  9. else {
  10. result = INPlacemarkResolutionResult.unsupported()
  11. }
  12. }
  13. else {
  14. // Ask for the drop-off location.
  15. result = INPlacemarkResolutionResult.needsValue()
  16. }
  17. // Return the result.
  18. completion(result)
  19. }

Because your Intents extension is launched on demand, your INExtension subclass and handler objects should maintain as little state information as possible. SiriKit usually asks you to provide a handler object several times during the processing of a single intent. It may take time to resolve all the parameters of an intent, especially if Siri must ask the user to clarify parameters or provide additional information. Asking for a fresh handler object each time guarantees that the object is always valid. It also means that the methods of your handlers should not rely on cached state information. Instead, they should always perform fresh requests for any needed data.

For information on how to implement this method, see INIntentHandlerProviding Protocol Reference.

Resolving the Parameters of an Intent

The resolve phase is your opportunity to validate the parameters of an intent and make sure you have the information you need to continue. Because requests coming from Siri involve spoken commands, the initial information provided by the user may be incomplete or ambiguous. During the resolve phase, you can look at each parameter and let Siri know if the provided value is satisfactory or if you need Siri to ask further questions of the user.

Most intents have at least one parameter for your handler object to resolve. When implementing your handler, it is recommended that you implement all of the resolution methods for a given intent. Even if you do not use a given parameter, you can use the corresponding resolve method to tell SiriKit specifically that you do not use it. Telling SiriKit that you do not use a parameter prevents it from asking the user any questions about that parameter.

The structure of each resolve method you write should be the same:

  1. Get the parameter value from the intent object.

  2. Validate the value and match it to data that your app uses.

  3. Instantiate the appropriate INIntentResolutionResult subclass with your resolution.

  4. Execute the provided completion block with your resolution result object.

The Intents framework defines subclasses of INIntentResolutionResult for each data type that can be used as a parameter. Each subclass contains class methods for specifying whether you resolved the data successfully or needed more information. The parent class INIntentResolutionResult also contains methods you use for common resolution results. Table 3-1 lists the possible resolutions and when you might use each one.

Table 3-1Possible outcomes when resolving intent parameters

Resolution

Examples

You successfully matched the value.

Specify this resolution if you can use the provided value to handle the intent. You should always strive toward a resolution of parameters, even if that means making educated guesses about what the user meant. For example, if the user specified the name “John” for a person and has only one contact with that name, you could resolve the parameter successfully to that contact.

See the documentation for the appropriate resolution result object for the name of the method to use for successful results.

A value was not required.

Specify this resolution when you do not need the value of the parameter to handle the intent. For example, you might return this result when the intent contains a goal for an open workout, which normally has no goal.

Use the notRequired class method to create your resolution result object.

Values need disambiguation.

Specify this result when you identify two or more possible results and cannot choose one definitively. This result may lead to additional interactions with the user to choose one of the provided values.

See the documentation for the appropriate resolution result object for the name of the method to use for disambiguating results.

The value needs user confirmation.

Specify this result when you want the user to confirm the value you chose. Use this result when you identify a value, but you are not completely sure that it is the right value. For example, you might use this result when the value you chose is different than the one the user provided but you think the new value is what the user intended. Siri always asks the user to confirm values for some types of intents, such as those involving financial transactions.

See the documentation for the appropriate resolution result object for the name of the method to use for confirming results.

A value is required.

Specify this result when the value is missing for a parameter that you require. For example, you might return this result if the user requests a payment from another person but does not specify the payment amount.

Use the needsValue class method to create your resolution result object.

The value is unsupported.

Specify this result when your app does not support a specific value or when that value conflicts with the values of other parameters. For example, you might return this result when the user wants to send a payment in Swiss francs but your app requires transactions to be in Euros or US Dollars.

Use the unsupported class method to create your resolution result object.

When resolving parameters, the Intents framework continues to call your resolution method until you respond with either a success or notRequired resolution result. Any other result triggers additional interactions with the user. The intents framework then calls your resolution method again to resolve the updated information.

Try to reach a successful resolution as quickly as possible. Asking for more information incurs delays and might frustrate the user. Instead, try to choose reasonable values based on the user’s patterns and habits, and ask for disambiguation or confirmation only as needed.

Listing 3-2 shows an example from a ride booking app that validates the drop-off location for the ride. If a drop-off location was provided and is within the app’s service area, the method returns a successful result. If the value is outside the app’s service area, the method lets SiriKit know that the value is unsupported. If no value is provided, the method asks for one.

Listing 3-2Resolving a required parameter of an intent

Objective-C

  1. - (void)resolveDropOffLocationForRequestRide:(INRequestRideIntent *)intent
  2. withCompletion:(void (^)(INPlacemarkResolutionResult *resolutionResult))completion {
  3. CLPlacemark* location = intent.dropOffLocation;
  4. INPlacemarkResolutionResult* result = nil;
  5. if (location) {
  6. // If the location is valid, use it; otherwise,
  7. // let the user know it is not supported
  8. if ([self locationIsInsideServiceArea:location])
  9. result = [INPlacemarkResolutionResult successWithResolvedPlacemark:location];
  10. else
  11. result = [INPlacemarkResolutionResult unsupported];
  12. }
  13. else {
  14. // Ask for the drop-off location.
  15. result = [INPlacemarkResolutionResult needsValue];
  16. }
  17. // Return the result.
  18. completion(result);
  19. }

Swift

  1. func resolveDropOffLocation(forRequestRide intent: INRequestRideIntent,
  2. with completion: (INPlacemarkResolutionResult) -> Void) {
  3. let location = intent.dropOffLocation
  4. var result : INPlacemarkResolutionResult = nil
  5. if location != nil {
  6. // If the location is valid, use it; otherwise,
  7. // let the user know it is not supported
  8. if self.locationIsInsideServiceArea(location) {
  9. result = INPlacemarkResolutionResult.successWithResolvedValue(location)
  10. }
  11. else {
  12. result = INPlacemarkResolutionResult.unsupported()
  13. }
  14. }
  15. else {
  16. // Ask for the drop-off location.
  17. result = INPlacemarkResolutionResult.needsValue()
  18. }
  19. // Return the result.
  20. completion(result)
  21. }

Confirming a Request

Use the confirm phase of an intent to perform any final validation of the intent parameters and to verify that you are ready to handle the intent. SiriKit asks you to confirm an intent after all parameters have been resolved satisfactorily. You can use the confirm method to perform one final check. If your app relies on network-based services, you also use the confirm method to verify that those services are available. Implementing confirm methods is not required but is strongly recommended.

In your confirm method, perform any confirmation tasks and then create a response object with the data that you intend to use when handling the intent. If the user is prompted for confirmation, Siri displays the details from your response object in the confirmation interface. Siri always prompts the user to confirm important requests, especially those that are irrevocable or involve financial transactions. Users may not be prompted in other situations.

Listing 3-3 shows a simple confirmation method for starting a workout. This method provides a response indicating that the app is ready to start the workout. This method specifies nil for the user activity object, which causes SiriKit to create a default user activity object.

Listing 3-3Confirming the start of a workout

Objective-C

  1. - (void)confirmStartWorkout:(INStartWorkoutIntent *)startWorkoutIntent
  2. completion:(void (^)(INStartWorkoutIntentResponse * _Nonnull))completion {
  3. // Make sure the app is ready.
  4. // Provide the response.
  5. INStartWorkoutIntentResponse* response = [[INStartWorkoutIntentResponse alloc]
  6. initWithCode:INStartWorkoutIntentResponseCodeReady userActivity:nil];
  7. completion(response);
  8. }

Swift

  1. func confirmStartWorkout(startWorkout intent: INStartWorkoutIntent,
  2. completion: (INStartWorkoutIntentResponse) -> Void) {
  3. // Make sure the app is ready.
  4. // Provide the response.
  5. let response = INStartWorkoutIntentResponse(code: .ready, userActivity: nil)
  6. completion(response)
  7. }

Handling a Request

The final stage of handling an intent is to perform the task associated with that intent. The handle method of your handler object has two responsibilities:

  • Perform the task associated with the intent.

  • Return a response object with information about what your app did.

How you handle an intent depends on the intent itself. Whenever possible, handle an intent directly from your Intents extension. However, some intents involve passing control back to your app. For example, the successful handling of a start workout intent involves launching your app to start the workout. For other intents, take whatever steps are required to perform the task. After performing the task, put the details of what you did into the response object that you return to SiriKit.

Listing 3-4 shows the method for starting a new workout. In this case, the handler tells Siri to launch the associated app to start the workout. Other intents might require providing additional data with the response object.

Listing 3-4Handling the start of a workout

Objective-C

  1. - (void)handleStartWorkout:(INStartWorkoutIntent *)startWorkoutIntent
  2. completion:(void (^) (INStartWorkoutIntentResponse * _Nonnull))completion {
  3. INStartWorkoutIntentResponse* response = [[INStartWorkoutIntentResponse alloc]
  4. initWithCode:INStartWorkoutIntentResponseCodeContinueInApp userActivity:nil];
  5. completion(response);
  6. }

Swift

  1. func handleStartWorkout(startWorkout intent: INStartWorkoutIntent,
  2. completion: (INStartWorkoutIntentResponse) -> Void) {
  3. let response = INStartWorkoutIntentResponse(code: .continueInApp, userActivity: nil)
  4. completion(response)
  5. }

Each response object supports an optional NSUserActivity object with details about how to launch your app. If you do not specify this object, SiriKit automatically creates one for you and inserts an INInteraction object containing the intent and your response. If you want to provide additional information, you can create the NSUserActivity object yourself and add information to its userInfo dictionary. Your parent app should be prepared to handle these user activity objects.

Changes you make when handling an intent should also be reflected in your app’s interface. As part of performing a task, your Intents extension should communicate any relevant information back to its parent app by providing a custom NSUserActivity object in its responses. If your Intents extension handles the intent successfully, the user activity object may never be delivered. However, if there is a problem, the user activity object ensures that your app has the information it needs to proceed.

When SiriKit cannot handle the interaction, it launches your app as follows:

Implement these methods and respond to the data provided by the user activity.

Tips for Handling Intents

When providing a response object in your confirm or handle methods, consider the following:

  • Always include as much information as possible in your responses. Siri and Maps communicate as much information as possible to the user when confirming or handling intents. Filling your response object with detailed information creates a better user experience by clearly communicating what your app did.

  • Configure data objects fully before assigning them to the response object. For most response objects, properties use copy semantics. If you get the object in a property and modify it, the changes you make happen on the copy, not the original. Therefore, you must always set the value of the property only after making your changes.

  • Return your response objects within a few seconds. Because Siri and Maps are actively communicating with the user, always return your response as quickly as possible. If it will take more than a few seconds to return your response, return your response with an “in-progress” code to indicate that you are still working on the request.

  • Use custom user activity objects to support deep linking into your app. Providing a custom NSUserActivity object, instead of relying on the default object, lets you supply additional information for configuring your user interface. You can use that information to display specific information about the task that the user performed in Siri or Maps, which yields a better user experience.

  • Always handle user activity objects in your parent app. Whenever Siri or Maps need to transfer control to your app, they provide an NSUserActivity object with information about what happened. Response objects create a default NSUserActivity object if you do not provide one yourself. Handling these objects in your app ensures a seamless experience for the user.

SiriKit leverages information from the user’s contacts database to fill in parameters when applicable. If your app was denied access to the user’s contacts, information related to those contacts may not be included with the intent. For example, any INPerson objects in the intent may contain only a value in their spokenPhrase property and not contain any other contact information. This lack of access also prevents SiriKit from accessing the addresses of contacts, which causes a phrase like “I need a ride home using <app>” to result in an intent without a drop-off location. If your app was denied access to the user’s contacts, you might want to inform the user that you can provide a better experience with access restored.