public: true title: WWDC 2022/Implement App Shortcuts with App Intents Date: [[Jun 8th, 2022]] Conference: [[WWDC 2022]] Speaker(s): [[Michael Sumner]] Tags: [[Talks]] URL: - ## [[Overview]] - People use shortcuts to create multi-step workflows - Before now, someone had to make an app shortcut via Siri or in the Shortcuts app - In iOS 16 - Available as soon as your app is installed - App Shortcuts can be run from Shortcuts, Spotlight. and Siri - **When searching in Spotlight, it will rank right away** - But not if it launches your app - ## [[Implement App Shortcuts]] - [[App Shortcuts]] are built with [[App Intents]] - [[Swift]] only framework - App Intents are implemented in Swift - `AppShortcutsProvider` defines phrases for your [[Shortcuts]] - **Each app can have a maximum of 10 App Shortcuts** - Implement an AppIntent - ```swift // StartMeditationIntent creates a meditation session. import AppIntents struct StartMeditationIntent: AppIntent { static let title: LocalizedStringResource = "Start Meditation Session" func perform() async throws -> some IntentResult & ProvidesDialog { await MeditationService.startDefaultSession() return .result(dialog: "Okay, starting a meditation session.") } } ``` - Create an `AppShortcutsProvider` - - ```swift // An AppShortcut turns an Intent into a full fledged shortcut // AppShortcuts are returned from a struct that implements the AppShortcuts // protocol import AppIntents struct MeditationShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: StartMeditationIntent(), phrases: ["Start a \(.applicationName)"] ) } } ``` - Using the token allows Siri to also look for App Name synonyms you’ve configured - Provide multiple phrases - ```swift // An AppShortcut turns an Intent into a full fledged shortcut // AppShortcuts are returned from a struct that implements the AppShortcuts // protocol import AppIntents struct MeditationShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: StartMeditationIntent(), phrases: [ "Start a \(.applicationName)", "Begin \(.applicationName)", "Meditate with \(.applicationName)", "Start a session with \(.applicationName)" ] ) } } ``` - Provide a dialog and snippet view - Custom views in App Intents - Leverage [[SwiftUI]] - Can’t include interactivity or animations - Can display custom UI at multiple steps - To display custom UI, return your view from the intent - ```swift // Custom views give your intent more personality // and can convey more information func perform() async throws -> some ProvidesDialog & ShowsSnippetView { await MeditationService.startDefaultSession() return .result( dialog: "Okay, starting a meditation session.", view: MeditationSnippetView() ) } ``` - Implementing an AppEntity - ```swift // An entity is a type that can be used as a parameter // for an AppIntent. import AppIntents struct MeditationSession: AppEntity { let id: UUID let name: LocalizedStringResource static var typeDisplayName: LocalizedStringResource = "Meditation Session" var displayRepresentation: AppIntents.DisplayRepresentation { DisplayRepresentation(title: name) } static var defaultQuery = MeditationSessionQuery() } ``` - Query for Entities - ```swift // Queries allow the App Intents framework to // look up your entities by their identifier struct MeditationSessionQuery: EntityQuery { func entities(for identifiers: [UUID]) async throws -> [MeditationSession] { return identifiers.compactMap { SessionManager.session(for: $0) } } } ``` - Define a parameter - ```swift // Adding a parameter to an intent allows you to prompt the user // to provide a value for the parameter struct StartMeditationIntent: AppIntent { @Parameter(title: "Session Type") var sessionType: SessionType? // ... } ``` - Prompt for values - 3 types of promps - **Disambiguation**: Choose from a fixed list - **Value**: Ask the user for a freeform value like a string or int - **Confirmation**: Double check with the user about what they mean - They slow down the conversation, so use sparingly. - see: [[WWDC 2022/Design App Intents]] - ```swift // Prompting for values can be done by calling methods // on the property's wrapper type. func perform() async throws -> some ProvidesDialog { let sessionToRun = self.session ?? try await $session.requestDisambiguation( among: SessionManager.allSessions, dialog: IntentDialog("What session would you like?") ) } await MeditationService.start(session: sessionToRun) return .result( dialog: "Okay, starting a \(sessionToRun.name) meditation session." ) } ``` - Parameterized phrases - App Shortcuts support predefined parameters - **Example**: "start a *walking* meditation" - Not meant for open ended values - Can’t support "Search my diary for x" as an initial query - Implement `suggestedEntities()` - ```swift // Queries can provide suggested values for your Entity // that serve as parameters for App Shortcuts struct MeditationSessionQuery: EntityQuery { func entities(for identifiers: [UUID]) async throws -> [MeditationSession] { return identifiers.compactMap { SessionManager.session(for: $0) } } func suggestedEntities() async throws -> [MeditationSession] { return SessionManager.allSessions } } ``` - Update App Shortcut parameters whenever your app’s model changes - ```swift // Your app must notify App Intents when your values change // This is typically best done in your app’s model layer class SessionModel { @Published var sessions: [MeditationSession] = [] private var cancellable: AnyCancellable? init() { self.cancellable = $sessions.sink { _ in MeditationShortcuts.updateAppShortcutParameters() } } // ... } ``` - Add parameterized phrases - ```swift // Phrases can also contain a single parameter reference import AppIntents struct MeditationShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: StartMeditationIntent(), phrases: [ "Start a \(.applicationName)", "Begin \(.applicationName)", "Meditate with \(.applicationName)", "Start a \(\.$session) session with \(.applicationName)", "Begin a \(\.$session) session with \(.applicationName)", "Meditate on \(\.$session) with \(.applicationName)" ] ) } } ``` - - ## Add discoverability - Natural phrases - Great phrases are short and memorable - Consider using your app name as a noun or verb - App name synonyms are supported - Siri Tip - Discoverability is vital for users - [[Siri Tip]] replaces the [[Add to Siri button]] - Avaiable in [[SwiftUI]] and [[UIKit]] - Best placed contextually - Order status check after placing an order - Supports dismissal - Calls a custom closure when tapped, remove it from the layout and don’t show it again until relevant - Shortcuts Link - `ShortcutsLink` opens the shortcuts app to a filtered list of your shortcuts - App Installation - App Shortcuts appear as soon as your app is installed - Your intent should fail gracefully if it’s run before the user is logged in - Parameter values won’t be gathered until you notify App Instents - If you have no parameterized phrases, the user won’t see them until you run the app - Siri now supports - "What can I do here?" - "What can I do with **AppName**?" - Ordering determined by the order in your shortcut - Put best and most-used shortcuts and phrases first in the lists -