WWDC Quick Look đź’“ By SwiftGGTeam
Bring your app's core features to users with App Intents

Bring your app's core features to users with App Intents

Watch original video

Highlight

App Intents is the unified protocol layer for Siri, Spotlight, Shortcuts, Widgets, Control Center, and all system integrations—define Intent, Entity, and Query once and drive multiple system entry points.


Core Content

No matter how good your app is, users see nothing if they don’t open it. Christopher Nebel opens with a blunt metaphor: every app is a carefully crafted “box” with a perfect in-box experience, but switching between boxes is friction. Switching apps takes time and attention; repeated small friction breaks the user’s flow.

App Intents addresses this. It lets the system understand your app’s core actions and content objects, then surface them in Spotlight search suggestions, Siri conversations, Home Screen widgets, Control Center controls, the Action Button, Apple Pencil Pro squeeze, and more. Users can complete key actions without opening your app first. The session demos a hiking trail app throughout: starting with the simplest OpenPinnedTrailIntent, expanding to parameterized intents, configurable widgets, Control Center buttons, and App Shortcuts—showing how the same Entity and Query reuse across features.

The key insight is code reuse. The TrailEntity and TrailEntityQuery you define for OpenTrail Intent work directly in widget configuration parameters, Control Center button configuration, and Siri natural language. Write once, use everywhere.


Detailed Content

Intent: defining actions (09:31)

An Intent is a type conforming to AppIntent with two requirements: title (localized, shown as the action name in Shortcuts) and perform() (does the work).

struct OpenPinnedTrailIntent: AppIntent {
    static var title: LocalizedStringResource = "Open Pinned Trail"

    static var openAppWhenRun = true

    func perform() throws -> some IntentResult {
        // Navigate to the pinned trail detail page.
        TrailNavigator.navigateToPinnedTrail()
        return .result()
    }
}

Key points:

  • title is localized; Shortcuts uses it as the action name
  • openAppWhenRun = true tells the system to bring the app to the foreground when run
  • perform() returns IntentResult; here .result() is empty because navigation is enough
  • Name intents with user-understandable actions (OpenPinnedTrail), not internal implementation names

Entity + Query: content and lookup (12:13)

An Entity is a “noun” in your app—a core content object. It conforms to AppEntity with displayRepresentation, id, and query.

struct TrailEntity: AppEntity {
    static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Trail")
    static var defaultQuery = TrailEntityQuery()

    var id: UUID
    var name: String

    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(name)")
    }
}

Query answers two questions: what Entities are available? Which Entity matches an ID? The simplest path is EnumerableEntityQuery with allEntities():

struct TrailEntityQuery: EntityQuery, EnumerableEntityQuery {
    func allEntities() async throws -> [TrailEntity] {
        // Return all trails; suitable when the dataset can be fully loaded into memory.
        TrailStore.shared.allTrails.map { trail in
            TrailEntity(id: trail.id, name: trail.name)
        }
    }

    func entities(for identifiers: [UUID]) async throws -> [TrailEntity] {
        // Look up by ID to restore parameters when the Intent runs.
        allEntities().filter { identifiers.contains($0.id) }
    }
}

Key points:

  • Built with iOS 18 SDK, EnumerableEntityQuery can automatically derive search, predicate queries, and more (14:18)
  • If data is too large for memory, use other sub-protocols like EntityStringQuery
  • Query’s entities(for:) lets the system restore saved IDs to full Entity objects at Intent runtime—you get Entities, not bare IDs

Parameterized Intent: OpenIntent (11:04)

When an Intent needs a user-selected content object, use OpenIntent:

struct OpenTrailIntent: OpenIntent {
    static var title: LocalizedStringResource = "Open Trail"

    @Parameter(title: "Trail")
    var target: TrailEntity

    func perform() throws -> some IntentResult {
        TrailNavigator.navigateToTrail(id: target.id)
        return .result()
    }
}

Key points:

  • OpenIntent implies openAppWhenRun—no manual declaration needed
  • @Parameter properties appear in Shortcuts parameter UI; Query supplies options
  • Parameter type should be the Entity (TrailEntity), not descriptive data (name or UUID)

Parameter Summary: readable Shortcuts descriptions (15:00)

static var parameterSummary: some ParameterSummary {
    Summary("Open \.$target")
}

Key points:

  • Without parameterSummary, parameters collapse below the list in Shortcuts
  • With it, actions read like “Open Monterey Bay Coastal Trail”
  • Placeholders update live while editing parameters

Widget and Control Center: reuse Entities (16:59)

Widget configuration intents conform to WidgetConfigurationIntent and reuse TrailEntity as a parameter. Control Center configurable controls use ControlConfigurationIntent:

extension OpenTrailIntent: ControlConfigurationIntent {}
// Empty extension; OpenTrailIntent already has everything it needs.

Key points:

  • One Intent can conform to multiple configuration protocols—no separate Intents for Widget and Control Center
  • Control Center button taps can pass a configured Intent instance—it already has perform() and parameter values

App Shortcut: automatic Spotlight and Siri (20:40)

struct TrailAppShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: OpenPinnedTrailIntent(),
            phrases: [
                "Open my pinned trail in \(.applicationName)"
            ],
            shortTitle: "Open Pinned Trail",
            systemImageName: "pin.fill"
        )
    }
}

Key points:

  • App Shortcut wraps an Intent instance, not a type—you can pre-fill parameters at registration
  • phrases must include \.applicationName for Siri voice matching
  • No manual registration—the framework detects AppShortcutsProvider automatically; works after install
  • Existing App Shortcuts automatically support Apple Pencil Pro squeeze this year and Action Button last year (22:30)

Return Dialog + Snippet instead of opening the app (22:56)

When Siri runs an Intent to show information rather than navigate, return Dialog and a SwiftUI view to avoid launching the app:

struct ShowPinnedTrailIntent: AppIntent {
    static var title: LocalizedStringResource = "Show Pinned Trail Conditions"

    func perform() async throws -> some IntentResult & ProvidesDialog & ShowsSnippetView {
        let trail = TrailStore.shared.pinnedTrail
        return .result(
            dialog: "\(trail.name) conditions: \(trail.conditions)",
            view: TrailConditionsView(trail: trail)
        )
    }
}

Key points:

  • ProvidesDialog lets Siri speak text; ShowsSnippetView shows a SwiftUI snippet on screen
  • Dialog and view text can differ—spoken vs visual emphasis
  • Views use the same archiving as widgets—only widget-supported SwiftUI features
  • After the snippet, users stay in place instead of being thrown into your app

Core Takeaways

  • What to do: Define AppEntity + EntityQuery for core content models. Why: Entities are the foundation for all App Intents features—define once and Shortcuts parameters, widget config, Control Center config, and Siri understanding come free. How to start: Pick your app’s 1–2 core data models, conform to AppEntity, implement displayRepresentation, id, and EnumerableEntityQuery.

  • What to do: Turn your highest-frequency actions into App Shortcuts. Why: App Shortcuts work on install, appear in Spotlight and Siri with zero user setup—best ROI integration. How to start: Find 1–2 most-used actions (“open recent document,” “view favorites”), wrap with AppShortcutsProvider, write phrases and shortTitle.

  • What to do: Drive multiple system entry points with one Intent. Why: The session’s OpenTrailIntent powers Shortcuts, widget config, Control Center buttons, and Siri from one implementation. One Intent conforming to multiple protocols (OpenIntent + WidgetConfigurationIntent + ControlConfigurationIntent) covers all scenes. How to start: Migrate existing SiriKit intents or custom URL handlers to App Intents, then extend with protocol conformances for widgets and Control Center.


Comments

GitHub Issues · utterances