SiriKit required defining intents in a separate .intentdefinition file, generating bridged handler classes, and implementing a protocol matching a fixed catalog of domains — you couldn't just expose an arbitrary app action.
#The old way
import Intents
class OrderCoffeeIntentHandler: NSObject, OrderCoffeeIntentHandling {
func handle(intent: OrderCoffeeIntent, completion: @escaping (OrderCoffeeIntentResponse) -> Void) {
completion(OrderCoffeeIntentResponse(code: .success, userActivity: nil))
}
}
#The new way
import AppIntents
struct OrderCoffeeIntent: AppIntent {
static var title: LocalizedStringResource = "Order Coffee"
static var description = IntentDescription("Places a coffee order for pickup.")
@Parameter(title: "Size")
var size: CoffeeSize
func perform() async throws -> some IntentResult {
try await CoffeeOrderService.shared.placeOrder(size: size)
return .result()
}
}
enum CoffeeSize: String, AppEnum {
case small, medium, large
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Coffee Size"
static var caseDisplayRepresentations: [CoffeeSize: DisplayRepresentation] = [
.small: "Small",
.medium: "Medium",
.large: "Large"
]
}
#Why it matters
- No
.intentdefinitionfile, no generated bridging headers — the intent is just a Swift struct conforming to a protocol - The same
AppIntentpowers Siri, Shortcuts, Spotlight, and widget or control interactions from one implementation @Parametertypes drive both the system's UI for filling in the intent and its validation, with no hand-written parsing- Intents built this way can be surfaced as App Shortcuts without a separate donation call for every use
#Gotcha
perform() runs without any guarantee your app's UI is on screen, or even that the app is foregrounded — treat it like a background entry point, not a button handler, and don't assume @MainActor view state is safe to touch without hopping explicitly. Also, changing a published intent's parameter names or types after it ships can break existing Shortcuts users have already built with it, since Shortcuts stores references to specific parameters. This API surface has grown a lot since its iOS 16 introduction, so double-check exact protocol requirements against current docs before shipping.