Open Search

Refresher for how to set up Siri Shortcuts

October 21, 2018 2:45 pm
Categorised in:
Reading Time: 4 minutes

I’ve recently made my life easier by setting up some Siri Shortcuts in my Stereo Remote app. As I have a tendency to forget things, I’ve jotted down some notes on how to implement Siri Shortcuts into an app. Be warned: This isn’t a tutorial for these things, although I link to some good ones. This is just an overview, a simple guide, to use more like a checklist when creating Shortcuts for an app.

About Siri Shortcuts

Apple first announced and demoed Siri Shortcuts at WWDC 2018. Siri Shortcuts allow the system to present app-suggested shortcut commands to the user. Users can then choose to create their own shortcut phrase to trigger Shortcuts via Siri. Requests can be understood & implemented by most Siri capable devices (macOS seeming to being an outlier), thanks to iCloud syncing.

Creating Siri Shortcuts

This tutorial is useful for anyone looking for a more in-depth explanation of how to create Intents/Shortcuts. Basically, to create Shortcuts you need to follow these steps:

  • Create an Intents file
  • Add an Intent Handler target to your app
  • Turn on Siri Capabilities in Project Settings
  • Move all code that will be required to be accessed by your Intents into a shared framework
  • Donate Intents for the functionality you require

Common troubleshooting when creating Siri Shortcuts

  • Ensure Intents are added to your Intent handlers targets plist
  • Insure parameters you need to access from the users request are set in the Intent are add to the **whatever** required
  • Don’t forget to import Intents in any classes that require Intents functionality, either in your shared framework or in your view controller classes

Handling Intents

When you add an Intent Target, Xcode gives you a bunch of boilerplate. The only function you really need to care about is the handler for, if you have more than one intent, you will probably want to pass an instance of the appropriate class, unless all your parameters are the same, you won’t be able to grab them.



override func handler(for intent: INIntent) -> Any {

    switch (intent){

        case is ChangeSourceIntent:

            return FirstIntentHandler()

        case is ChangePowerIntent:

            return SecondIntentHandler()

        default:

            fatalError("Unhandled intent type: \(intent)")

        }
    
}


The two functions required for your intent handler to work are confirm(intent:_, completion:_) and handle(intent:_, completion:_).

Confirm(intent:_, completion:_)

Use this to confirm you can do whatever, there is stock, details are set, can connect, and also confirm user activity, does want to order.

Handle(intent:_, completion:_)

Use this to implement whatever, you can pass in information to be used in the response as user activity here. You also want to grab any references to parameters here to pass in as user activity for the visual response.

Visual Response

When you created your Intent Handler target, there will have been an option, ticked by default to create an associated View Controller for you. If you don’t provide a view controller, Siri will provide one that with space for an app icon and a string. These can be populated with either the app icon or an image passed in as user activity, so a relevant image to the user request. The string defaults to what you set up in the Intents file, but again can be passed in as UserActivity. Apple uses the example of showing tomato soup when a user orders tomato soup, rather than a generic icon.

example of a Siri Shortcut response with a view controller
 alt=
Provide your own View Controller for the response, or the use the system default

This makes sense, the user already knows they are using your app, plus it’s signposted elsewhere in this visual response. So where possible populate these two response areas with information the user will find useful.

If you do use a ViewController you can receive user activity passed from the handle(intent:_, completion:_) method.

Donating Intents

All of this does nothing if the system isn’t exposed to the Intent. The easiest way to do this is to donate the interaction when the user activates it.



func donateInteraction(name: String, id: NSNumber) {

        let intent = MyIntent()

        //adding intent parameters

        intent.myIntentParameterExampleName = name

        intent.myIntentParameterExampleID = id

        //Intents API - this is phrase to be shown as an example when the user records their shortcut phrase
               
        intent.suggestedInvocationPhrase = "Execute my shortcut baby"

        
let interaction = INInteraction(intent: intent, response: nil)

        interaction.donate { (error) in

            if error != nil {

                if let error = error as NSError? {

                    print("Interaction donation failed: error: \(error)")

                } else {

                    print("Successfully donated interaction")

                }

            }

        }

    }


Donations can appear in 3 places, the lockscreen, search results & in the Siri section of Settings app.

example of a Siri Shortcut showing up on lock screen
example of Siri Shortcuts showing up in search results
example of Siri Shortcut in Settings app
Provide your own View Controller for the response, or the use the system default

You can supply specific images, user info, title & subtitle strings as user activity



let userActivity = NSUserActivity(activityType: "com.markcormack.stereo-remote.source-change")
userActivity.isEligibleForSearch = true
userActivity.isEligibleForPrediction = true
userActivity.title = "title"
userActivity.userInfo = ["key": "value"]
userActivity.suggestedInvocationPhrase = "invocate me"
        
let attributes = CSSearchableItemAttributeSet(itemContentType: "<#T##String#>")
let image = UIImage(named: "")
attributes.thumbnailData = image?.pngAsData
attributes.contentDescription = "subtitle"
userActivity.contentAttributeSet = attributes



Deleting ***Donations

***** To delete stuff you basically need to ****
When you create your UserActivity, you have the opportunity to add a persistent identifier so that that particular activity can be deleted from the search results at a later date. To do this, first add a persistent identifier to your activity.



let persistentIdentifier = NSUserActivityPersistentIdentifier("identifier")
userActivity.persistentIdentifier = persistentIdentifier

This can activity can then be deleted later using:



NSUserActivity.deleteSavedUserActivities(withPersistentIdentifiers: ["my identifier"]){ () in
            
}

To delete all ****donations from search results, use:



      NSUserActivity.deleteAllSavedUserActivities {
            //
        }

Suggesting shortcuts for the Siri watch face

Shortcuts can be suggested to show up on the Siri watch face. There isn’t a requirement to have a Watch app for this to happen, they are powered by Apples Proactive thing. You pass your intent into a INShortcut and pass that into a INRelevantShortcut. You add relevance providers to INRelevantShortcut and then try to setRelevantShortcuts to the INRelevantShortcutStore



var relevantShortcuts:[INRelevantShortcut] = []
func suggest(intent: INIntent){
    // Add an intent to the list of suggestions. To create
    // a shortcut from an intent, the intent must be valid.
    if let shortcut = INShortcut(intent: intent){
        let relevantShortcut = INRelevantShortcut(shortcut: shortcut)
        relevantShortcut.shortcutRole = INRelevantShortcutRole.action
        let location = INDailyRoutineRelevanceProvider.Situation.home
        let locationProvider = INDailyRoutineRelevanceProvider(situation: location)
        let time = INDailyRoutineRelevanceProvider.Situation.evening
        let timeProvider = INDailyRoutineRelevanceProvider(situation: time)
        relevantShortcut.relevanceProviders = [locationProvider, timeProvider]    
        relevantShortcuts.append(relevantShortcut)
     }
        
     INRelevantShortcutStore.default.setRelevantShortcuts(relevantShortcuts) { (error) in
        if let error = error {
            print("Failed to set relevant shortcuts. \(error)")
        } else {
           print("Relevant shortcuts set.")
        }
    }
}

This joint was penned by @elmarko