$npx -y skills add dpearson2699/swift-ios-skills --skill app-intentsImplement App Intents for Siri, Shortcuts, Spotlight, widgets, Control Center, and Apple Intelligence on iOS. Covers AppIntent actions, AppEntity and EntityQuery models, AppShortcutsProvider phrases, IndexedEntity Spotlight indexing, WidgetConfigurationIntent, SnippetIntent, and
| 1 | # App Intents (iOS 26+) |
| 2 | |
| 3 | Implement, review, and extend App Intents to expose app functionality to Siri, |
| 4 | Shortcuts, Spotlight, widgets, Control Center, and Apple Intelligence. |
| 5 | |
| 6 | ## Contents |
| 7 | |
| 8 | - [Triage Workflow](#triage-workflow) |
| 9 | - [AppIntent Protocol](#appintent-protocol) |
| 10 | - [`@Parameter`](#parameter) |
| 11 | - [AppEntity](#appentity) |
| 12 | - [EntityQuery (4 Variants)](#entityquery-4-variants) |
| 13 | - [AppEnum](#appenum) |
| 14 | - [AppShortcutsProvider](#appshortcutsprovider) |
| 15 | - [System Surface Integration](#system-surface-integration) |
| 16 | - [iOS 26 Additions](#ios-26-additions) |
| 17 | - [Common Mistakes](#common-mistakes) |
| 18 | - [Review Checklist](#review-checklist) |
| 19 | - [References](#references) |
| 20 | |
| 21 | ## Triage Workflow |
| 22 | |
| 23 | ### Step 1: Choose the action, boundary, and surface |
| 24 | |
| 25 | Start from 1-3 valuable actions people want outside the app, not from its screen hierarchy. Record one design row per action: user goal; inline result or app destination; shared domain operation; parameters and entity query; confirmation/authentication; target surface and protocol. Use one explicit runtime route for app handoff instead of scattering navigation side effects through intents. |
| 26 | |
| 27 | Then choose the system feature and protocol that fit that action: |
| 28 | |
| 29 | | Surface | Protocol | Since | |
| 30 | |---|---|---| |
| 31 | | Siri / Shortcuts | `AppIntent` | iOS 16 | |
| 32 | | Configurable widget | `WidgetConfigurationIntent` | iOS 17 | |
| 33 | | Control Center | `ControlConfigurationIntent` | iOS 18 | |
| 34 | | Spotlight search | `IndexedEntity` | iOS 18 | |
| 35 | | Apple Intelligence | `@AppIntent(schema:)` | iOS 18 | |
| 36 | | Interactive snippets | `SnippetIntent` | iOS 26 | |
| 37 | | Visual Intelligence | `IntentValueQuery` | iOS 26 | |
| 38 | |
| 39 | ### Step 2: Define the data model |
| 40 | |
| 41 | - Prefer `AppEntity` shadow models for app data exposed to the system. |
| 42 | - Create `AppEnum` types for fixed parameter choices. |
| 43 | - Choose the right `EntityQuery` variant for resolution. |
| 44 | - Mark searchable entities with `IndexedEntity` and `indexingKey` metadata. |
| 45 | |
| 46 | ### Step 3: Implement the intent |
| 47 | |
| 48 | - Conform to `AppIntent` (or a specialized sub-protocol). |
| 49 | - Declare `@Parameter` properties for all user-facing inputs. |
| 50 | - Implement `perform() async throws -> some IntentResult`. |
| 51 | - Add `parameterSummary` for Shortcuts UI. |
| 52 | - Register phrases via `AppShortcutsProvider`. |
| 53 | |
| 54 | ### Step 4: Verify |
| 55 | |
| 56 | - Build and run in the target system surface to confirm discovery, parameter resolution, cancellation, confirmation, authentication, result rendering, and app handoff. |
| 57 | - If a step fails, reset the fixture, fix the smallest intent/entity/query boundary, and rerun the same action from the same surface before adding another action. |
| 58 | - Test Siri phrases with the intent preview in Xcode. |
| 59 | - Confirm `IndexedEntity` instances are indexed in a named Spotlight index. |
| 60 | - Check widget configuration for `WidgetConfigurationIntent` intents. |
| 61 | |
| 62 | ## AppIntent Protocol |
| 63 | |
| 64 | The system instantiates the struct via `init()`, sets parameters, then calls |
| 65 | `perform()`. Declare a `title` and `parameterSummary` for Shortcuts UI. |
| 66 | |
| 67 | ```swift |
| 68 | struct OrderSoupIntent: AppIntent { |
| 69 | static var title: LocalizedStringResource = "Order Soup" |
| 70 | static var description = IntentDescription("Place a soup order.") |
| 71 | |
| 72 | @Parameter(title: "Soup") var soup: SoupEntity |
| 73 | @Parameter(title: "Quantity", default: 1) var quantity: Int |
| 74 | |
| 75 | static var parameterSummary: some ParameterSummary { |
| 76 | Summary("Order \(\.$soup)") { \.$quantity } |
| 77 | } |
| 78 | |
| 79 | func perform() async throws -> some IntentResult { |
| 80 | try await OrderService.shared.place(soup: soup.id, quantity: quantity) |
| 81 | return .result(dialog: "Ordered \(quantity) \(soup.name).") |
| 82 | } |
| 83 | } |
| 84 | ``` |
| 85 | |
| 86 | Optional members: `description` (`IntentDescription`), `openAppWhenRun` (`Bool`), |
| 87 | `isDiscoverable` (`Bool`), `authenticationPolicy` (`IntentAuthenticationPolicy`). |
| 88 | |
| 89 | ## `@Parameter` |
| 90 | |
| 91 | Declare each user-facing input with `@Parameter`. Non-optional parameters are |
| 92 | required; the system requests values when needed. Defaults pre-fill a useful |
| 93 | value. Optional parameters are not requested automatically, so ask for them in |
| 94 | `perform()` when the intent cannot continue without a value. |
| 95 | |
| 96 | ```swift |
| 97 | // Required; the system asks for a value when needed |
| 98 | @Parameter(title: "Count") |
| 99 | var count: Int |
| 100 | |
| 101 | // Required and pre-filled |
| 102 | @Parameter(title: "Count", default: 1) |
| 103 | var count: Int |
| 104 | |
| 105 | // Optional; request it yourself if it becomes necessary |
| 106 | @Parameter(title: "Count") |
| 107 | var count: Int? |
| 108 | ``` |
| 109 | |
| 110 | ### Supported value types |
| 111 | |
| 112 | Primitives: `Bool`, `Int`, `Double`, `String`, `Duration`, `Date`, `Decimal`, |
| 113 | `Measurement`, and `URL`. Collections: `Array` and `Set` of supported element |
| 114 | types. Framework: `IntentPerson`, `IntentFile`. Custom: any `AppEntity` or |
| 115 | `AppEnum |