What are App Intents, App Entities and App Shortcuts, how do they replace SiriKit and Intents extensions, and how do you test them?
Predict first
An intent is created in code, its parameter set and perform() called, with no Siri involved. What do the title and parameter hold?
import AppIntents
struct Greet: AppIntent {
static let title: LocalizedStringResource = "Greet"
@Parameter(title: "Name") var name: String
func perform() async throws -> some IntentResult & ReturnsValue<String> { .result(value: "Hello, \(name)") }
}
var g = Greet()
g.name = "Asha"
_ = try await g.perform()
print(Greet.title.key, g.name)
- Greet Asha
- Hello, Asha Asha
- Greet nil
Mental model. App Intents is a menu you hand the system: “here are the dishes my kitchen can make (intents), here are the ingredients I keep (entities), and here are the phrases that order the specials (shortcuts)”. Siri, Spotlight, the Action button and widgets all read the same menu.
App Intents (iOS 16+) is a Swift-only framework for exposing your app’s functionality to the system with no extension target and no intent-definition file. An AppIntent is a struct with a static title, optional description, @Parameter properties (typed inputs the system can collect from the user, from Siri or from a Shortcut), and an async throws perform() that does the work and returns an IntentResult (optionally with a value, a dialog or a SwiftUI snippet view: .result(value:), .result(dialog:)). An AppEntity describes a thing the user cares about (a note, a playlist, a recipe) with an identifier, a display representation and an EntityQuery that can fetch entities by id, suggest them, and search; entities appear as parameter choices, in Spotlight and in Shortcuts. An AppShortcutsProvider publishes ready-made App Shortcuts with spoken phrases ("Add a note in \(.applicationName)") that work in Siri, Spotlight and the Action button immediately after install, without the user building a shortcut. The same intent can power a widget button, a control, a Live Activity button and a Spotlight action, which is the reason to centralise logic in intents. Because intents are plain Swift types, you can test them directly: create the struct, set its parameters and try await intent.perform(), asserting on the result and the state change, with no UI or Siri involved (Greet with name “Asha” can be created, configured and awaited in a unit test).
Where it runs and how it fits. By default an intent runs in your app’s process, launched in the background if needed, without bringing the app to the foreground unless openAppWhenRun is true (or it returns an OpensIntent); an intent compiled into an App Intents extension target, or into a widget extension, runs in that extension’s process with its limits, which is cheaper for simple actions. Other design points: give parameter summaries (ParameterSummary) so Shortcuts shows readable actions; return dialogs and values for Siri; validate parameters and fail with throw and a localised error; add IndexedEntity so entities flow into Spotlight; adopt the system’s assistant schemas (the @AppIntent(schema:) and @AppEntity(schema:) macros for domains such as books, notes and photos) where they fit, so system intelligence features understand your content; request confirmation or authentication (requestConfirmation(), authenticationPolicy) for sensitive actions; and keep intents idempotent and quick. SiriKit and the Intents extension (an extension point with an INExtension, an Intents definition file and the domain-specific intents for messaging, payments, workouts, ride booking, lists and notes, and VoIP calls) is the legacy model: it still works for domains App Intents does not cover yet and for existing apps, but it is more ceremony (resolve, confirm and handle phases, a separate target, an intent-definition editor) and Apple’s direction is App Intents, which also replaces NSUserActivity shortcuts and donated INInteractions in most cases. For a new app, start with App Intents; for an old one, migrate intents one at a time (the framework can bridge from an existing definition).
An intent is an ordinary struct: its static title and its parameter are readable, and perform() can be awaited directly in a test.
Common traps
- Writing separate logic for the app, the widget and Shortcuts. Centralise it in intents and share the model.
- Starting a new project on SiriKit and an Intents extension. It is the legacy route; use App Intents unless a domain is missing.
They might follow up with
- How do you unit test an intent? Create it, set its parameters and
await perform(); it is a plain Swift type. - How do App Shortcuts differ from ordinary shortcuts? They are published by the app with phrases and work immediately after install.
Related questions
- 19.4.2 How do configurable widgets, interactive buttons and Control Center controls work with App Intents?
- 19.2.1 How do Notification Service and Notification Content extensions work: mutable content, the deadline…
- 17.5.2 How do Core Spotlight, NSUserActivity, Handoff and universal links make an app’s content discoverable and…