app-intents
Write and review Swift code that exposes app functionality through the App Intents framework, ensuring correct protocol conformance, safe data flow, and idiomatic discoverability wiring.
Review process:
- Check intent fundamentals (protocol,
perform(), return types, dialog) usingreferences/fundamentals.md. - Validate parameters and parameter options using
references/parameters.md. - Validate entity types, display representations, and queries using
references/entities.md. - Check the
AppShortcutsProviderregistration and discoverability UI usingreferences/shortcuts-and-siri.md. - Validate
OpenIntentnavigation and snippet view return types usingreferences/open-and-snippet-intents.md. - Check dependency injection and the data-controller pattern using
references/dependencies.md. - Validate Spotlight indexing via
IndexedEntityand attribute sets usingreferences/spotlight.md. - Check
@AssistantEntity/@AssistantIntentschema adoption usingreferences/assistant-schemas.md. - If writing or reviewing tests for intents, use
references/testing-intents.md. - Catch common mistakes using
references/anti-patterns.md.
If doing partial work, load only the relevant reference files.
Task-based routing
Match the user's goal to the read order. Load only what you need.
"Create my first App Intent"
references/fundamentals.md- protocol,perform(), return typesreferences/shortcuts-and-siri.md- register it so it shows up
"Add a parameter the user picks in Shortcuts"
references/parameters.md-@Parameter, prompts, disambiguationreferences/entities.md- if the parameter is a domain entity
"Make my app's data searchable from Siri / Spotlight"
references/entities.md-AppEntity,IndexedEntity,@Propertyreferences/spotlight.md- indexing strategies, attribute setsreferences/shortcuts-and-siri.md- flexible matching, phrase rules
"Show a summary / interactive view when Siri runs my intent"
references/open-and-snippet-intents.md- inline vs indirect snippets,SnippetIntent,Button(intent:)references/fundamentals.md- return types and snippet design rules
"Open a specific thing in my app from Siri / Shortcuts"
references/open-and-snippet-intents.md-OpenIntent,URLRepresentableIntent,TargetContentProvidingIntentreferences/dependencies.md- navigator / scene routing
"Integrate with Apple Intelligence"
references/assistant-schemas.md- schema macros, domains, Use Model action, onscreen contentreferences/entities.md-@Property,Transferable(required for several schemas)
"Support Visual Intelligence / image search"
references/assistant-schemas.md-IntentValueQuery,SemanticContentDescriptor,@UnionValuereferences/entities.md-Transferable+OpenIntentpairing
"Build an interactive widget or Control Center control"
references/open-and-snippet-intents.md-Button(intent:),WidgetConfigurationIntent,ControlConfigurationIntent,ControlWidgetButtonreferences/dependencies.md- App Group shared storage, process boundaries
"Structure my app around App Intents"
references/fundamentals.md- intents as canonical action layerreferences/dependencies.md-@Dependency, cross-moduleAppIntentsPackagereferences/entities.md- entity-as-bridge pattern
"Test my App Intents"
references/testing-intents.md- unit tests, mocking@Dependency, what you can't test
"I'm hitting build errors or runtime bugs"
references/anti-patterns.md- 35+ catches with before/after fixes
Decision trees
Quick orientation when you know the task but not the right API.
Which intent protocol should I conform to?
Generic action?
→ AppIntent
Opens the app to a specific entity?
→ OpenIntent (+ TargetContentProvidingIntent on iOS)
Renders an interactive view only, no business logic?
→ SnippetIntent
Needs to bring the app forward conditionally?
→ AppIntent with supportedModes (iOS 26+)
→ or ForegroundContinuableIntent (iOS 17-18)
Deletes entities with standard confirmation?
→ DeleteIntent
Routes a search query into the app?
→ ShowInAppSearchResultsIntent
Backs widget configuration?
→ WidgetConfigurationIntent (empty conformance, no perform)
Backs a Control Center control?
→ ControlConfigurationIntent
Has a universal-link URL representation?
→ URLRepresentableIntent + OpenIntent (no perform needed)
Matches an Apple Intelligence domain?
→ AppIntent + @AppIntent(schema: .domain.action)
Which entity type should I use?
Fixed set known at compile time?
→ AppEnum
Dynamic data with a persistent id?
→ AppEntity
Entity must appear in Spotlight?
→ AppEntity + IndexedEntity
Entity IS a file (scan, voice memo, exported image)?
→ FileEntity
Computed / aggregated data with no stable id?
→ TransientAppEntity
Needs Apple Intelligence schema awareness?
→ AppEntity + @AppEntity(schema: .domain.type)
Supports cross-app sharing?
→ AppEntity + Transferable
Which query should my entity use?
Small fixed set, enumerable?
→ EnumerableEntityQuery (also gets a basic Find intent)
Large dataset, searchable by name?
→ EntityQuery + EntityStringQuery
Many queryable properties, user should build predicates?
→ EntityPropertyQuery (auto-generates Find intent with comparators + sort)
Simple id-only lookup, no search?
→ UniqueIDEntityQuery
Visual intelligence / image search?
→ IntentValueQuery + SemanticContentDescriptor
Which property wrapper should I use on entity fields?
Stored on the entity struct, should appear in Shortcuts/Find/summary?
→ @Property
Derived from an underlying model object (cheap to compute)?
→ @ComputedProperty (preferred over @Property for wrappers around models)
Derived AND should be indexed in Spotlight?
→ @ComputedProperty(indexingKey: \.keyName)
Expensive to compute (network, ML inference, heavy query)?
→ @DeferredProperty (async getter, only runs when system asks)
Internal to the entity, not shown anywhere?
→ plain stored property (no wrapper)
Core Instructions
- Target iOS 16+ / macOS 13+ minimum for App Intents.
IndexedEntity,OpenIntent, focus filters, and control widgets require iOS 16+;@AssistantEntity/@AssistantIntentschemas require iOS 18.2+; anchored relative date styles and many assistant schemas require iOS 18.4+. - Never make a SwiftData
@Modelclass or other reference-type data model conform toAppEntity.AppEntityrequiresSendable;@Modelclasses are not sendable. Create a separatestructentity that shadows the fields you want to expose. - Never pass
ModelContextacross actor boundaries.ModelContextis not sendable. PassModelContainer(which is sendable) and create a local context inside the actor that needs one. - Never expose an intent to Siri/Spotlight only by writing its type, always register it through an
AppShortcutsProvider. Types not registered there will not appear in Shortcuts, Siri suggestions, or the action button picker. - Never write a Siri activation phrase without interpolating
\(.applicationName). The App Intents macro rejects phrases without it at compile time, because phrases without the app name would collide with other apps' commands. - Never reach back into a SwiftUI
@Queryfrom inside an intent.@Queryonly works inside aView. Run a one-shotFetchDescriptorthrough aModelContextinstead, or route through a centralized data controller. - Never instantiate services, data stores, or authentication managers inside
perform(). Inject them through@Dependencyand register them once inApp.init()withAppDependencyManager.shared.add(dependency:). - Never use
String(format:)or manual concatenation for localized intent dialog. UseLocalizedStringResource, and use Foundation's grammar-agreement markdown (^[\(count) item](inflect: true)) inside anAttributedStringfor pluralization. - Prefer
OpenIntentfor "take me to this thing" actions,AppIntent & ShowsSnippetViewfor self-contained one-shot summaries, andAppIntent & ShowsSnippetIntent+ a pairedSnippetIntentwhen the snippet containsButton(intent:)and needs to re-render after buttons fire. - Always set
static let isDiscoverable: Bool = falseon helper intents that only back a widget button, snippet button, or other intent - otherwise they pollute the user's Shortcuts library. - When an intent mutates data that widgets or control widgets display, call
WidgetCenter.shared.reloadAllTimelines()insideperform()before returning. - When
Button(intent:)lives inside a widget view, share state between the intent (runs in the app process) and the widget's timeline provider (runs in the extension process) via App GroupUserDefaults(suiteName:)or a sharedModelContainerURL - neverUserDefaults.standardor in-memory@Dependencystate. - When entity data that appears in a shortcut phrase's key path changes (creation, rename, deletion), call
YourShortcutsProvider.updateAppShortcutParameters()to invalidate the cached candidate list. - Prefer
EnumerableEntityQuerywhen the whole set is small and cheap to load; implementEntityQuery+EntityStringQuerywhen the dataset is large or searchable; addEntityPropertyQueryto get a system-generated Find intent for free.entities(for identifiers:)is mandatory on every query; without it, parameter resolution breaks. - Mark entity fields that users might filter, sort, or reference in parameter summaries with
@Property(stored) or@ComputedProperty(derived). Plain stored properties are invisible to the App Intents framework. - Use
TransientAppEntityfor return data that's computed on the fly (summaries, aggregates). Don't try to shoehorn it intoAppEntitywith a fake id. - When the entity already has a universal-link URL, conform it to
URLRepresentableEntityand conform the open intent toURLRepresentableIntent- the system routes opens through your existing link handler without aperform(). - For entities visible onscreen that Siri should be able to understand, combine
.userActivity(_:element:)on the view withTransferableconformance on the entity. Identification alone is not enough; Siri needs exportable content. SnippetIntent.perform()is called multiple times per user interaction (initial show, after each button tap, on appearance changes, onreload()). Keep it pure: read state, assemble the view, return. Never mutate app state or kick off slow work inside a snippet intent'sperform().- Respect hard limits: 10
AppShortcuts per app, 1000 total trigger phrases (including parameter expansions). The first phrase in each shortcut's array is the primary one - it's shown on the Shortcuts home tile and as Siri's "what can I do with X?" answer. - Include at least one non-parameterized phrase per App Shortcut so it's discoverable before first launch; parameterized phrases don't appear in Spotlight until the app has run once and populated the parameter cache.
- Prefer
supportedModes+continueInForeground(iOS 26+) overForegroundContinuableIntent/needsToContinueInForegroundErrorfor new code that conditionally foregrounds. The newer form lets one intent declare it may run background or foreground based on runtime state. - Snippet views have a 340-point height ceiling; beyond this, scrolling breaks the glance overlay model. Link to the full app for deep content, and keep snippet text larger than system defaults for legibility at reading distance.
- For text parameters that may receive input from Apple Intelligence's Use Model action, declare the type as
AttributedStringrather thanStringso rich formatting (bold, italic, lists, tables) is preserved losslessly. - The app's
Appstruct initializer is executed when an intent runs, even if the UI never appears. Do all intent-relevant setup (ModelContainercreation,AppDependencyManager.shared.add(...), log plumbing) insideinit(), not inside view modifiers like.taskor.onAppear. LocalizedStringResourceis the standard string type everywhere in App Intents (titles, dialog, parameter prompts). It shares string catalogs with SwiftUI, so localization works out of the box.- Grammar agreement (
inflect: true) works in English, French, German, Italian, Spanish, and Portuguese (both variants). For other locales it falls back to the unmodified form.
Output Format
If the user asks for a review, organize findings by file. For each issue:
- State the file and relevant line(s).
- Name the anti-pattern being replaced.
- Show a brief before/after code fix.
Skip files with no issues. End with a prioritized summary of the most impactful changes to make first.
If the user asks you to write or fix intent code, make the changes directly instead of returning a findings report.
Example output:
RecentItemsIntent.swift
Line 18: SwiftData @Model cannot conform to AppEntity (not Sendable).
// Before
extension Article: AppEntity { ... }
// After
struct ArticleEntity: AppEntity {
var id: UUID
var title: String
static let typeDisplayRepresentation: TypeDisplayRepresentation = "Article"
static let defaultQuery = ArticleEntityQuery()
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(title)")
}
}
Line 42: Siri phrase missing \(.applicationName) - will fail to build.
// Before
AppShortcut(intent: OpenArticleIntent(), phrases: ["Open an article"], ...)
// After
AppShortcut(intent: OpenArticleIntent(), phrases: ["Open an article in \(.applicationName)"], ...)
Summary
- Sendability (high):
ArticleasAppEntitywill fail to compile on Swift 6; create a shadow struct. - Siri phrases (high):
\(.applicationName)is required in every phrase.
End of example.
References
references/fundamentals.md-AppIntentprotocol,perform(), return types (IntentResult,ProvidesDialog,ReturnsValue,ShowsSnippetView,OpensIntent), intent dialog, grammar agreement.references/parameters.md-@Parameter, primitive vs entity parameters,@AppEnum, dialog options, parameter prompts.references/entities.md-AppEntity,IndexedEntity, shadow-struct pattern, display representations, entity queries (EnumerableEntityQuery,EntityQuery,EntityStringQuery,UniqueIDEntityQuery).references/shortcuts-and-siri.md-AppShortcutsProvider, phrases (\(.applicationName)rule),shortcutTileColor,SiriTipView,ShortcutsLink, parameter presentation.references/open-and-snippet-intents.md-OpenIntent, snippet views (ShowsSnippetView), navigation via data controller, when to use which.references/dependencies.md-@Dependency,AppDependencyManager, data-controller pattern,ModelContainervsModelContextsendability, main-actor vs local-context tradeoff.references/spotlight.md-IndexedEntity,CSSearchableIndex,attributeSet, index-on-launch vs index-on-change, debounced reindexing.references/assistant-schemas.md-@AssistantEntity,@AssistantIntent, schema adoption, Xcode code snippets, caveats.references/testing-intents.md- unit testing intents, mocking@DependencyviaAppDependencyManager, Swift Testing patterns, what you can't unit-test.references/anti-patterns.md- common mistakes LLMs make when generating App Intents code.