Siri shortcuts are a new feature introduced in iOS 12 that allow the user to trigger an action in your app via a “Hey Siri” phrase. Shortcuts can also be triggered by a tap on a shortcut from the lock screen, search screen, etc.
In a previous post we created a simple Siri shortcut demo app using an NSUserActivity-based approach. In this post we develop a more complex Intents-based app.
Download the demo app source from GitHub: https://github.com/russell-archer/ShowRandomColor
What are Siri Shortcuts and how do I create them?
Siri shortcuts are a new feature introduced in iOS 12 that allow the user to trigger an action in your app via a “Hey Siri” phrase. Shortcuts can also be triggered by a tap on a shortcut shown from the lock screen, search screen, etc.
“Hey Siri, show me today’s events in MySportsApp”
Before proceeding, if you’re not familiar with creating Siri shortcuts and you haven’t yet read my previous post Siri Shortcuts using NSUserActivity, I suggest you look at that first for an overview of the subject, then return here for details on Intents.
Overview of how to implement Siri shortcuts
There are two approaches to creating Siri shortcuts:
- Simple to implement
- Open your app and trigger an action via a shortcut or Hey Siri phrase
- Little customisation
- More complex than NSUserActivity
- You can customise the experience for the user
- Open an app extension in Siri via a Hey Siri phrase
- Include custom responses from Siri and a custom UI that Siri will present
In this post we’ll concentrate on the Intents-based approach.
Overview of the demo app
We’ll create a demo app that allows the user to request a random color. The color can be displayed either:
- In the ShowRandomColor app, or
- By tapping on a Siri shortcut (color is displayed in Siri via an app extension), or
- Via a Hey Siri phrase (color is displayed in Siri via an app extension)
Create a new single-view iOS app named ShowRandomColor:
Select the app target and the Capabilities tab, then turn on support for Siri. This adds an .entitlements file to your project and allows you to use to Siri SDK:
Defining a Siri Intent
1. Custom Intents
Make sure the Intent is selected, not the Response. This allows us to define the form of the shortcut.
2. Custom Intent
Category. Select View which best describes what type of action our shortcut represents.
Title. Type a title that will appear on the shortcut.
Description. Provide a description for the shortcut.
Default Image. Select one of the images added to the asset catalog. This will appear on the shortcut.
Confirmation. Don’t check this as our action isn’t of the type that the user will need to confirm (e.g. a purchase, deleting an item, etc.).
Make sure that Supports background execution is checked. This allows the intent to run outside of the app, hosted in Siri.
Now select the Response part of the custom intent. This defines how Siri will respond when our shortcut is run.
Defines how Siri will respond when the shortcut is run.
Define one or more properties (variables) that can be used in the various types of response.
Add a colorName property of type String. This will be our app’s description of the random color displayed to the user
3. Response Templates
Create two response templates, one for failure and one for success. Notice how both templates use the colorName property.
Now switch back to the Intent view of our custom intent:
Build the project. Xcode will generate the following:
public class ShowRandomColorIntent: INIntent
Encapsulates the user’s request
public class ShowRandomColorIntentResponse: INIntentResponse
Encapsulates the app extension’s response
public protocol ShowRandomColorIntentHandling: NSObjectProtocol
Implement this protocol in the app extension to confirm the intent and handle the request
public enum ShowRandomColorIntentResponseCode: Int
Enum used for response codes (success, failure, etc.)
To view the source generated by Xcode (with the intent definition file still selected) select the Identity inspector.
Click the reveal arrow next to Custom Class Name:
This shows the directory where the generated code is stored:
Create the app’s UI
We can now create the (extremely simple) UI in the main app.
In Interface Builder, add a UIView and then add constraints so it fills the containing view (within the safe area).
Set its background to some random color:
We now need a means of getting a random color so we can set colorView.backgroundColor to that color. We need to make use of this in all of the following situations:
- When the app starts we set a random color during viewDidLoad(), or
- When the user taps on our Siri Shortcut which invokes our app extension to display a random color, or
- When the user activates our app extension with a Hey Siri phrase we display a random color
We need to design our app so that code (see Frameworks below) and data (see App Groups below) can be shared between the app, the app extension, and the app extension UI. So, for example, when a random color is generated by (say) the app extension we want the app extension UI to be able to see the value of that random color.
App extensions allow you to host portions (extensions) of your app in other apps. For example, with Siri shortcuts you can get Siri to host an app extension and a custom UI extension.
Even though an app extension bundle is physically delivered inside the containing app’s bundle, when executing an app extension and the containing app have no access to each other’s processes or containers (a container may be regarded as a sandboxed file system). Normally, the containing app is not even running when the app extension is running.
If you want to share data between a containing app and its app extensions and app extension UI the solution is to use an App Group.
An app group creates a secure, shared container that multiple processes can access:
The shared container can be used to exchange data using either UserDefaults or by directly working with files using FileManager. In both cases you need to use the app group identifier.
Adding an App Group to the project
Now select the next target which needs to share data and turn on app groups support. This time you’ll be able to select the the previously created group:
Repeat this process until all targets that require it have app groups support turned on. We can now use UserDefaults to share data (a random color value) between the app, the app extension, and the app extension UI as required.
We’ve seen how we can use App Groups to share data, but what about sharing code between the app, app extension and app extension UI?
The easiest (but definitely not the best) method is to create a ColorGenerator class and copy its .swift file into all three modules. Obviously, this is not a great way to share and reuse code. First, you have three copies of the code. If you need to make changes you always have to remember to make the exact same change to each copy of the class. If not, things get out of sync and you can end up with some very hard to find bugs and inconsistencies. Plus, it’s a lot more work to make changes (three times as much).
The best solution is to use a Framework. Examples of common frameworks are Foundation, UIKit, AVFoundation, CloudKit, etc.
Adding a Framework to our project
Select File > New > Target and choose the Cocoa Touch Framework template:
Add code to the framework
Using the ColorGenerator framework in the main app
We also get a couple of warnings:
Frameworks in App Extensions
To configure an app extension target to use an embedded framework, set the target’s “Require Only App-Extension-Safe API” build setting to Yes. If you don’t, Xcode reminds you to do so by displaying the warning “linking against dylib not safe for use in application extensions”.
More details about APIs forbidden in app extensions are available here:
To remove the warnings we need to rebuild the framework with the Require Only App-Extension-Safe API build setting set to Yes:
Donating the Intent in the main app
Adding the app extension code
Called prior to asking the app to handle the intent. The app should return a response object that contains additional information about the intent, which may be relevant for the system to show the user prior to handling. If unimplemented, the system will assume the intent is valid, and will assume there is no additional information relevant to this intent.
Defining the app extension UI
Adding the app extension UI code
Record a Hey Siri phrase
Tap the record button to finish:
Add support for the user tapping our shortcut
Tapping on the shortcut foregrounds the app and displays a random color: