← All shorts
67iOS 16+2 min read

App Intents: Making Your App Scriptable and Siri-Visible

App Intents replaces SiriKit's fixed intent domains with a Swift protocol any app action can conform to.

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 .intentdefinition file, no generated bridging headers — the intent is just a Swift struct conforming to a protocol
  • The same AppIntent powers Siri, Shortcuts, Spotlight, and widget or control interactions from one implementation
  • @Parameter types 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.

appintentsiossiri

Related shorts