How to implement Siri Shortcuts into health iOS application — Part one: Activity

Apple introduced a new powerful voice functionality with iOS 12 and upcoming iOS 13: Siri Shortcuts. This new Siri function gives developers the opportunity to create connections inside your applications that Siri uses to improve the end user’s experience by being able to use application functionalities without needing to open the app.

For more background info, check my previous article about how Siri Shortcuts reduce the friction between users and applications.

About Siri Shortcut

I won’t dig into the technical implementation here, but if you’re not yet familiar with Siri, check https://developer.apple.com/siri/ and https://developer.apple.com/documentation/sirikit

A nice example can be found here: https://developer.apple.com/documentation/sirikit/soup_chef_accelerating_app_interactions_with_shortcuts

I used these to develop my health application TakeURPill.

Siri Shortcuts have two flavours and each one has a specific use:

  • Activity
  • Intent

Activities are used to communicate to Siri when a user visualises a specific section of the application, so Siri can suggest this section in the future based on user behaviour. Through this suggestion the user can open a specific section of the application directly. The user can also create a custom voice command to open the section.

Intents are used to communicate to Siri when a user uses a specific function of the application. Like the Activity, Siri can suggest the intent and the user can handle the action without opening the application, also with a custom voice command.

In this article, I focus on Activities.

Before going in-depth, two important remarks:

  • Siri doesn’t open a specific section of the application by itself or runs a specific functionality, but only communicates to iOS to open the target application with a specific tag, so the application knows what to do.
  • Intent can handle a function without opening the application only if this Intent is marked as background intent by the developer.


Take Your Pill

TakeUrPill is a simple iOS application that helps you to keep track of the pills you took. It’s an open-source project which you can clone/fork from here.

It has only 3 functions:

  1. Set pill’s name and the amount you must take every time
  2. Register the pills you take on date and time
  3. View all the pills you took

Function #2 takes place within the homepage of the application, the other two happen in two different sections in the app.

Screen Shot 2018-08-14 at 16.15.10

(Home View)

 

Activity

A good use case for Activity in the TakeUrPill application is the History section. The user action related to this function is only to open and view a specific part of the application. So it would be great if the user can open the History right away by just asking Siri “Show my pills”.

The first step is to define an identity for the activity “the user opens the history view controller”. In the info.plist of your application, target add NSUserActivityTypes and add it to it a unique string to identify the activity, for example, com.mobiquityinc.demo.TakeUrPill.history:

 

1*ihnDLkG9vy_dK90cNWk_xw

(TakeURPill application info.plist)

Then you can create a specific service that handles the interaction between your presenter/view model and Siri. 


import Foundation import Intents struct SiriService { struct ActivityInformation { let activityType: String let activityTitle: String let activitySuggestedInvocation: String } static func donateInteraction(_ intent: INIntent, completion: ((Error?) -> Void)? = nil) { // Donate interaction to the system let interaction = INInteraction(intent: intent, response: nil) interaction.donate { error in completion?(error) } } static func activitySetup(_ information: ActivityInformation) -> NSUserActivity { // give our activity a unique ID let activity = NSUserActivity(activityType: information.activityType) // give it a title that will be displayed to users activity.title = information.activityTitle // allow Siri to index this and use it for voice-matched queries activity.isEligibleForSearch = true if #available(iOS 12.0, *) { activity.isEligibleForPrediction = true } // give the activity a unique identifier so we can delete it later if we need to if #available(iOS 12.0, *) { activity.persistentIdentifier = NSUserActivityPersistentIdentifier(information.activityType) } // You can also suggest the voice phrase that a user may want to use when adding a phrase to Siri activity.suggestedInvocationPhrase = information.activitySuggestedInvocation // make this activity active for the current view controller – this is what Siri will restore when the activity is triggered return activity } }


At this point, we need to focus only on ActivityInformationstruct and activitySetupfunction.

The struct is an easy way to pass, the details from the Presenter to SiriService about any Activities we wants to share with Siri, like this:


let information = SiriService.ActivityInformation( activityType: "com.mobiquityinc.demo.TakeUrPill.history", activityTitle: NSLocalizedString("activity.history", comment: ""), activitySuggestedInvocation: NSLocalizedString("activity.history.suggestion", comment: ""))


activityType is the string we defined in the info.plist, activityTitle is the title that will be displayed to users. The last one is a suggestion we give to the user about the voice command the user can associate to the activity.

The activitySetup function returns a NSUserActivity with all the information present into the ActivityInformation, information that Siri needs to present and to handle this activity. It’s important to set the new NSUserActivity property isEligibleForPrediction as true, otherwise, your activity will be not passed (“donate” is the official Apple term) to Siri!


activity.isEligibleForPrediction = true


The following shows how to donate your activity to Siri, starting from the viewDidAppear of the UIViewController you want to track as Siri Shortcut Activity. Important note: 

Apple suggests setting the userActivity property of your UIViewController in the viewDidAppear function.


final class HistoryViewController: BaseViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if let information = presenter?.information { activitySetup(information) } } }


extension UIViewController { func activitySetup(_ information: SiriService.ActivityInformation) { // make this activity active for the current view controller – // this is what Siri will restore when the activity is triggered self.userActivity = SiriService.activitySetup(information) } }


Now, every time the user opens the History UIViewController, the application donates the com.mobiquityinc.demo.TakeUrPill.history activity to Siri. The last thing to create is the logic to show the History UIViewController to the user.

This implementation can change based on the architecture of your application. In this case, TakeURPill adopts MVP with a FlowController and these are the steps:

  • Implement in the AppDelegate the function application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
  • Check the NSUserActivity, activityType and define a case for all the strings defined in the info.plist file
  • Trigger the logic to check the user state and show the History UIViewController. For demo purpose, I chose an easy implementation, with a Notification sent by the AppDelegate and observed by HomeViewController
  • The HomeViewController interacts with its Presenter and, if necessary, it communicates to its FlowController to start a new flow to the HistoryViewController

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { return handle(userAcitivity: userActivity) } // MARK: - Private private func handle(userAcitivity activity: NSUserActivity) -> Bool { switch activity.activityType { case "com.mobiquity.TakeUrPill.history": UserDefaults.standard.userSession = UserSession.History.rawValue let notificationName = Notification.Name(rawValue: "com.mobiquity.TakeUrPill.history") NotificationCenter.default.post(name: notificationName, object: nil) return true default:() } return false } // Function associated to the notification "com.mobiquity.TakeUrPill.history" // triggered from AppDelegate @objc func checkUserSession() { userSession() } // The presenter checks in which status the user is. If he is in the History, it's not // neccesary to show the History private func userSession() { if let presenter = presenter, presenter.showUserHistory() { showHistory() } } // This function just triggers the Flow Controller, that will bring the user to the // History section private func showHistory() { configure?.controller.show() }


1*8POme-t4NYNhQ13jBDt2GQ

(How the user can access directly the History of TakeUrPill from the Spotlight section)

Follow Mobiquity on Medium to receive a notification on the follow up article we will publish soon. This will focus on Siri Custom Intents, introduced with iOS 12, and Siri Conversational Shortcuts introduced with iOS 13.


Follow along!

@brain-bites

@darthpelo

Comments