How to Build Siri Actions with App Intents in iOS 16
The App Intents framework introduced in iOS 16 fundamentally changes
how developers integrate app capabilities with Siri and the Shortcuts
app. By replacing the legacy SiriKit Intents definition files with pure
Swift code, this framework allows developers to expose custom actions
natively without background extensions or complex code generation. This
guide details how developers implement the AppIntent
protocol, configure dynamic parameters, define natural voice phrases
using AppShortcutsProvider, and return visual responses to
users.
1. Adopt the AppIntent Protocol
To create an action Siri can run, define a Swift structure that
conforms to the AppIntent protocol. The protocol requires a
localized title and an asynchronous perform() method that
executes the action.
import AppIntents
struct SendMessageIntent: AppIntent {
static var title: LocalizedStringResource = "Send Message"
static var description = IntentDescription("Sends a quick message to a contact.")
func perform() async throws -> some IntentResult {
// Execute business logic here
return .result()
}
}2. Add Parameters with @Parameter
To enable Siri to collect necessary input from the user (such as
recipient names, text, or numbers), decorate properties with the
@Parameter property wrapper.
struct SendMessageIntent: AppIntent {
static var title: LocalizedStringResource = "Send Message"
@Parameter(title: "Recipient")
var recipientName: String
@Parameter(title: "Message Content")
var message: String
func perform() async throws -> some IntentResult {
MessageService.send(message, to: recipientName)
return .result()
}
}If a user invokes the intent without providing all required parameters, Siri automatically prompts the user for the missing values using conversational loops.
3. Expose Custom Data Using AppEntity
For complex types—such as items from a database, contacts, or media
collections—conform the data model to AppEntity. An
AppEntity requires an EntityQuery so Siri and
Shortcuts can look up objects by identifier or string search.
struct ContactEntity: AppEntity {
static var defaultQuery = ContactQuery()
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Contact"
var id: UUID
var name: String
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(name)")
}
}
struct ContactQuery: EntityQuery {
func entities(for identifiers: [UUID]) async throws -> [ContactEntity] {
// Fetch entities matching identifiers
}
func suggestedEntities() async throws -> [ContactEntity] {
// Return quick suggestions for UI pickers
}
}4. Provide Zero-Configuration App Shortcuts
In iOS 16, users no longer need to manually configure phrases in the
Shortcuts app before invoking them with Siri. Developers declare
ready-to-use voice commands by conforming a structure to
AppShortcutsProvider.
struct AppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: SendMessageIntent(),
phrases: [
"Send a message with \(.applicationName)",
"Message \(\.$recipientName) using \(.applicationName)"
],
shortTitle: "Send Message",
systemImageName: "message.fill"
)
}
}Whenever the app is installed or updated, iOS automatically indexes these phrases. Users can immediately speak phrases like, "Hey Siri, send a message with MyApp."
5. Return Dialog and Visual Feedback
The perform() method returns an instance conforming to
IntentResult. Developers can provide spoken responses,
visual SwiftUI dialogs, or value outputs directly in the Siri
interface.
func perform() async throws -> some IntentResult & ReturnsValue<Bool> & ProvidesDialog {
let success = MessageService.send(message, to: recipientName)
return .result(
value: success,
dialog: IntentDialog("Message sent to \(recipientName).")
)
}By leveraging these programmatic declarations, developers gain type safety, fast compile-time verification, and deep integration with iOS 16 system surfaces including Siri, Spotlight, and the Shortcuts app.