Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Siri Shortcut Introduction

Siri Shortcut Introduction

nalydadad

July 24, 2018
Tweet

More Decks by nalydadad

Other Decks in Programming

Transcript

  1. What is shortcut? • Shortcut let you expose the capabilities

    of your app to Siri. • It can accelerate the user to perform a key function of your app. • It should be of persistent interest to the user. • It should be executable at any time.
  2. User Engagement Siri Suggestion Voice Interaction Define Donate Handle Navigate

    to App Executes in background Create shortcut Input Output
  3. • Define NSUserActivity • Set -isEligibleForPrediction to true • Set

    -isEligibleForSearch to true first Donate • Donate to UIResponder to UIResponder
  4. // How to donate a shortcut let userActivity = NSUserActivity(activityType:

    "com.myapp.name.my-activity-type") userActivity.isEligibleForSearch = true userActivity.isEligibleForPrediction = true userActivity.title = "Activity" userActivity.userInfo = ["key": "value"] userActivity.suggestedInvocationPhrase = "Let's do it" let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String) let image = UIImage(named: "myImage")! attributes.thumbnailData = image.pngData() attributes.contentDescription = "Subtitle" userActivity.contentAttributeSet = attributes viewController.userActivity = userActivity
  5. // How to donate a shortcut let userActivity = NSUserActivity(activityType:

    "com.myapp.name.my-activity-type") userActivity.isEligibleForSearch = true userActivity.isEligibleForPrediction = true userActivity.title = "Activity" userActivity.userInfo = ["key": "value"] userActivity.suggestedInvocationPhrase = "Let's do it" let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String) let image = UIImage(named: "myImage")! attributes.thumbnailData = image.pngData() attributes.contentDescription = "Subtitle" userActivity.contentAttributeSet = attributes viewController.userActivity = userActivity Do not call becomeCurrent when donating to UIResponder
  6. // How to handle a shortcut func application(_ application: UIApplication,

    continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { if userActivity.activityType == "com.myapp.name.my-activity-type" { // Restore state for userActivity and userInfo } } Handle // How to donate a shortcut let userActivity = NSUserActivity(activityType: "com.myapp.name.my-activity-type") userActivity.isEligibleForSearch = true userActivity.isEligibleForPrediction = true userActivity.title = "Activity" userActivity.userInfo = ["key": "value"] userActivity.suggestedInvocationPhrase = "Let's do it" let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String) let image = UIImage(named: "myImage")! attributes.thumbnailData = image.pngData() attributes.contentDescription = "Subtitle" userActivity.contentAttributeSet = attributes viewController.userActivity = userActivity - continueUserActivity
  7. Define Siri Intent Go to File -> New File, and

    choose “SiriKit Intent Definition File.”
  8. Define Siri Intent Go to File -> New File, and

    choose “SiriKit Intent Definition File.”
  9. public class OrderSoupIntent: INIntent { public var items: [INObject]? public

    var deliveryLocation: CLPlacemark? } public protocol OrderSoupIntentHandling: NSObjectProtocol { public func handle(intent: OrderSoupIntent, completion: @escaping (OrderSoupIntentResponse) -> Void) optional func confirm(intent: OrderSoupIntent, completion: @escaping (OrderSoupIntentResponse) -> Void) } // Donate your shortcut let intent = PlaceOrderIntent() intent.items = order.items.map({ INObject(identifier: $0.id.uuidString, display: $0.menuItem.displayName) }) intent.deliveryLocation = order.destinationLocation let interaction = INInteraction(intent: intent, response: nil) interaction.donate { error in // Handle error } Code Generation public class OrderSoupIntent: INIntent { public var items: [INObject]? public var deliveryLocation: CLPlacemark? } public protocol OrderSoupIntentHandling: NSObjectProtocol { public func handle(intent: OrderSoupIntent, completion: @escaping (OrderSoupIntentResponse) -> Void) optional func confirm(intent: OrderSoupIntent, completion: @escaping (OrderSoupIntentResponse) -> Void) } • Siri Intent • Handling Protocol Define
  10. Donate // Donate your shortcut let intent = PlaceOrderIntent() intent.items

    = order.items.map({ INObject(identifier: $0.id.uuidString, display: $0.menuItem.displayName) }) intent.deliveryLocation = order.destinationLocation let interaction = INInteraction(intent: intent, response: nil) interaction.donate { error in // Handle error } // Donate your shortcut let intent = PlaceOrderIntent() intent.items = order.items.map({ INObject(identifier: $0.id.uuidString, display: $0.menuItem.displayName) }) intent.deliveryLocation = order.destinationLocation let interaction = INInteraction(intent: intent, response: nil) interaction.donate { error in // Handle error } to INInteraction
  11. • - continueUserActivityActivity • support background execution • Goto File

    -> new Target then create Intents Extension. • Check the target membership. The Intent should be visible to Intents Extension. • Make the Intents Extension confirm the Handling Protocol. Handle
  12. Handle // Handle your shortcut func application(_ application: UIApplication, continue

    userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { if userActivity.activityType == "OrderSoupIntent", let intent = userActivity.interaction?.intent as? OrderSoupIntent { // Show ordering UI, pre-populated with the fields from the intent } } // Donate your shortcut let intent = PlaceOrderIntent() intent.items = order.items.map({ INObject(identifier: $0.id.uuidString, display: $0.menuItem.displayName) }) intent.deliveryLocation = order.destinationLocation let interaction = INInteraction(intent: intent, response: nil) interaction.donate { error in // Handle error } - continueUserActivity
  13. // Handle your shortcut class IntentHandler: INExtension, OrderSoupIntentHandling { func

    confirm(intent: OrderSoupIntent, completion: (OrderSoupIntentResponse) -> Void) { completion(OrderSoupIntentResponse(code: .ready, userActivity: nil)) } func handle(intent: OrderSoupIntent, completion: (OrderSoupIntentResponse) -> Void) { // Order the soup completion(OrderSoupIntentResponse(code: .success, userActivity: nil)) } } Handle // Donate your shortcut let intent = PlaceOrderIntent() intent.items = order.items.map({ INObject(identifier: $0.id.uuidString, display: $0.menuItem.displayName) }) intent.deliveryLocation = order.destinationLocation let interaction = INInteraction(intent: intent, response: nil) interaction.donate { error in // Handle error } Intent Handling Protocol
 for background execution You should still handle -continueActivity, even though you have already confirmed Intent handling protocol
  14. Which one to choose? • Use NSUserActivity if your shortcut:

    • Opens something in your app • Represents showing items that you index in Spotlight or offer for Handoff • For the best experience, adopt Intents. Intents-based shortcuts can: • Run inline, without launching your app • Include custom voice response or a custom UI Include granular predictions
  15. // Custom response object public class OrderSoupIntentResponse : INIntentResponse {

    public var waitTime: String? { get set } public var soup: INObject? { get set } public convenience init(code: OrderSoupIntentResponseCode, userActivity: NSUserActivity?) public static func success(soup: INObject, waitTime: String) -> OrderSoupIntentResponse public static func failureOutOfStock(soup: INObject) -> OrderSoupIntentResponse } Code Generation Voice Response
  16. // Handle the intent class IntentHandler: INExtension, OrderSoupIntentHandling { func

    confirm(intent: OrderSoupIntent, completion: (OrderSoupIntentResponse) -> Void) { ... } func handle(intent: OrderSoupIntent, completion: (OrderSoupIntentResponse) -> Void) { guard let soup = intent.soup, let order = Order(from: intent) else { completion(OrderSoupIntentResponse(code: .failure, userActivity: nil)) return } let orderManager = SoupOrderDataManager() orderManager.placeOrder(order: order) completion(OrderSoupIntentResponse(code: .success, userActivity: nil)) } }
  17. • You had better prepared these 4 types of response:

    • Confirmation • Success • Informational • Error Voice Response By Apple’s suggestions.
  18. Optimizing Suggestions • The suggestion’s goal: Find Routines / Patterns

    • NSUserActivity • requiredUserInfoKeys: Minimal amount of information necessary for restoration which used to find patters. • INIntent • Shortcut types: Find the most appropriate parameter combination.
  19. Shortcut Testing Developer settings on device Set up a shortcut

    with Siri and edit the Xcode scheme to test Shortcut Testing
  20. Localizations • Localize both your code and the strings in

    your intent definition files • It will create all contents of all patterns. • Handle pluralization in stringsdict
  21. Deferred Localized Intents String Localization happens when the string is

    presented to the user Used when preparing the intent for donation intent.suggestedInvocationPhrase = NSString.deferredLocalizedIntentsString(with: “Go to Mars!")
  22. Default suggestion shortcut // How to suggest a shortcut for

    something a user hasn’t done yet import Intents let suggestions = [ INShortcut(intent: orderFavoriteSoupIntent), INShortcut(userActivity: orderFavoriteBeverageUserActivity), ] INVoiceShortcutCenter.shared.setShortcutSuggestions(suggestions) ! "
  23. Bring shortcut into your App “Chowder time” “Tomato tomato” Thanks

    for your order! $8, office Soup Chef # Your clam chowder will be delivered to One Apple Park Way soon. Done Done Add to Siri INUIAddVoiceShortcutViewController INUIEditVoiceShortcutViewController Available shortcuts list API?! See nothing in documents…
  24. Bring shortcut into your App // Adding a shortcut from

    an NSUserActivity import IntentsUI let userActivity = NSUserActivity(activityType: "com.unicorny.SoupChef.viewOrder") // Configure your NSUserActivity let shortcut = INShortcut(userActivity: userActivity) let viewController = INUIAddVoiceShortcutViewController(shortcut: shortcut) viewController.delegate = self present(viewController, animated: true)
  25. Media Shortcut Provide media playback shortcuts. • INPlayMediaIntent • Launches

    app in background • Suggested in playback controls on Lock Screen • Works on HomePod • INUpcomingMedia • Podcasts, TV shows
  26. • INRelevantShortcut • INRelevanceProvider • Background Task • iOS Relevant

    Shortcuts on Siri Watch Face Siri Shortcuts on the Siri Watch Face
  27. INRelevantShortcut // Relevant Shortcut open class INRelevantShortcut : NSObject, NSCopying,

    NSSecureCoding { @NSCopying open var shortcut: INShortcut { get } @NSCopying open var relevanceProviders: [INRelevanceProvider] @NSCopying open var watchTemplate: INDefaultCardTemplate? public init(shortcut: INShortcut) }
  28. Define // Setting INRelevantShortcuts let userActivity = NSUserActivity(activityType: "com.myapp.LogMeal") userActivity.userInfo["meal"]

    = "Dinner" let shortcut = INShortcut(userActivity: userActivity)
 let relevantShortcut = INRelevantShortcut(shortcut: shortcut!) let cardTemplate = INDefaultCardTemplate(title: "Log Dinner") cardTemplate.subtitle = "Beat the veggie challenge!" cardTemplate.image = INImage(named: "MenacingBroccoli") relevantShortcut.watchTemplate = cardTemplate let relevanceProvider = INDailyRoutineRelevanceProvider(situation: .evening) relevantShortcut.relevanceProviders = [relevanceProvider] INRelevantShortcutStore.default.setRelevantShortcuts([relevantShortcut]) { error in NSUserActivity
  29. Define INIntent // Setting INRelevantShortcuts let intent = LogFavoriteIntent() intent.favorite

    = "Cookies" intent.setImage(INImage(named: "Cookies"), forParameterNamed: "favorite") let shortcut = INShortcut(intent: intent)
 let relevantShortcut = INRelevantShortcut(shortcut: shortcut!) INRelevantShortcutStore.default.setRelevantShortcuts([relevantShortcut]) { error in if let error = error { /* */ } } // Setting INRelevantShortcuts let intent = LogFavoriteIntent() intent.favorite = "Cookies" intent.setImage(INImage(named: "Cookies"), forParameterNamed: "favorite") let shortcut = INShortcut(intent: intent)
 let relevantShortcut = INRelevantShortcut(shortcut: shortcut!) INRelevantShortcutStore.default.setRelevantShortcuts([relevantShortcut]) { error in if let error = error { /* */ } }
  30. INRelevantShortcut // Relevant Shortcut Store open class INRelevantShortcutStore : NSObject

    { open class var `default`: INRelevantShortcutStore { get } open func setRelevantShortcuts(_ shortcuts: [INRelevantShortcut], completionHandler: ((Error?) -> Void)? = nil) }
  31. Define setRelevantShortcuts // Setting INRelevantShortcuts let intent = LogFavoriteIntent() intent.favorite

    = "Cookies" intent.setImage(INImage(named: "Cookies"), forParameterNamed: "favorite") let shortcut = INShortcut(intent: intent)
 let relevantShortcut = INRelevantShortcut(shortcut: shortcut!) INRelevantShortcutStore.default.setRelevantShortcuts([relevantShortcut]) { error in if let error = error { /* */ } }
  32. Donate NSUserActivity Donations Set eligibleForPrediction and eligibleForSearch Supported activity types

    in Info.plist Donate when visible let userActivity = NSUserActivity(activityType: "MyActivityType") userActivity.becomeCurrent() let userActivity = NSUserActivity(activityType: "MyActivityType") interfaceController.updateUserActivity(activityType) NEW NSUserActivity
  33. Donate INIntent Donations Donate using INInteraction Indicate primary shortcut type

    let intent = /* Create intent and set parameters */ let interaction = INInteraction(intent: intent, response: nil) interaction.donate(completion: nil) INIntent
  34. Handle // Handle restoring from NSUserActivity class ExtensionDelegate: NSObject, WKExtensionDelegate

    { func handle(_ userActivity: NSUserActivity) { if userActivity.activityType == "com.myapp.LogMeal" { WKExtension.shared().rootInterfaceController?.popToRootController() WKExtension.shared().rootInterfaceController?.pushController(withName: "LogMealInterfaceController", context: userActivity.userInfo!["meal"]) } else if userActivity.activityType == "LogFavoriteIntent" { let interaction = userActivity!.interaction let intent = interaction.intent // restore } } } NEW
  35. Handle Background Task • WKRelevantShortcutRefreshBackgroundTask • Refresh data. • WKIntentDidRunRefreshBackgroundTask

    • It will be called after intent execution. • Update UI for snapshots or complication.
  36. INRelevanceProvider • Describe content relevance • Evaluated again time, location

    and routine • INDateRelevanceProvider • INLocationRelevanceProvider • INDailyRoutineRelevanceProvider • Provide array to INRelevantShortcut
  37. INDateRelevanceProvider //Relevance Providers open class INDateRelevanceProvider : INRelevanceProvider { open

    var startDate: Date { get } open var endDate: Date? { get } public init(start startDate: Date, end endDate: Date?) } let startEvent = /* event start time */ let endEvent = /* event end time */ let relevanceProvider = INDateRelevanceProvider(start: startEvent, end: endEvent)
  38. INLocationRelevanceProvider //Relevance Providers open class INLocationRelevanceProvider : INRelevanceProvider { @NSCopying

    open var region: CLRegion { get } public init(region: CLRegion) } let center = CLLocationCoordinate2D(latitude: 37.334728, longitude: -122.008883) let region = CLCircularRegion(center: center, radius: 2.0, identifier: "Apple Park") let relevanceProvider = INLocationRelevanceProvider(region: region)
  39. INDailyRoutineRelevanceProvider //Relevance Providers open class INDailyRoutineRelevanceProvider : INRelevanceProvider { public

    enum Situation : Int { case morning case evening case home case work case school case gym } open var situation: Situation { get } public init(situation: Situation) } let relevanceProvider = INDailyRoutineRelevanceProvider(situation: .gym)
  40. INDailyRoutineRelevanceProvider //Relevance Providers open class INDailyRoutineRelevanceProvider : INRelevanceProvider { public

    enum Situation : Int { case morning case evening case home case work case school case gym } open var situation: Situation { get } public init(situation: Situation) } let relevanceProvider = INDailyRoutineRelevanceProvider(situation: .gym)
  41. Summary • Define, Donate, Handle • Consider the informations you

    want to donate. • Enable the shortcut capability for your NSUserActivity and INIntent.