App Intents on macOS: System Actions, Parameters, and Shortcut Contracts
Expose dependable macOS app actions through App Intents with stable parameters, entity resolution, concise shortcuts, and idempotent execution.
App Intents gives an app a typed way to describe actions and data concepts to supported system experiences. On macOS, app actions built with AppIntent are available in the Shortcuts app, where people can add them to their own workflows. A separate feature called App Shortcuts - preconfigured shortcuts that the system can surface automatically - is not supported on macOS according to Apple’s current Human Interface Guidelines. Keep that platform distinction explicit: an intent action can be useful on a Mac without promising App Shortcut surfacing in Spotlight or Siri.
The best Mac intents expose a small number of valuable, complete actions. A Shortcuts workflow should work with explicit inputs, current account state, and a clear failure result. Do not expose every internal command as a system action. Prefer capabilities a person can describe without needing to know the app’s internal object graph.
Model an intent as an action contract
An AppIntent type declares an action, its parameters, metadata, and perform() implementation. Parameters should be sufficient to identify the work without relying on whichever row happens to be selected in a view. If an action affects a specific model, use a stable entity identifier or a resolvable AppEntity, not a transient index path.
import AppIntents
struct RebuildSearchIndexIntent: AppIntent {
static var title: LocalizedStringResource = "Rebuild Search Index"
static var description = IntentDescription(
"Rebuilds the searchable index for the current library."
)
func perform() async throws -> some IntentResult {
// Call the app's idempotent indexing service and report its outcome.
.result()
}
}
This is a compilable structural example with the action body intentionally left as a placeholder. It declares a Mac App Intent action; it does not register an App Shortcut. A real implementation should validate account access, distinguish no-op from failure, and return a result or dialog that accurately describes what happened. If the action can run more than once, design it to be idempotent or key the operation by a stable request identity.
Parameters must resolve to stable data
Use @Parameter for values the person or system must provide. The system can infer parameter values or ask the user for them before perform() runs. Include a parameter summary that reads naturally and exposes optional choices clearly. Avoid hidden required state such as “the selected document” unless the intent is specifically designed for the current selection and that selection is represented by the supported system context.
When an action operates on app data, define an AppEntity and an EntityQuery that can resolve identifiers, suggest likely entities, and enumerate supported values. Keep entity identifiers stable across renames and sync. A display name is not a durable identifier: duplicate names, localization, and user edits make it ambiguous. If a parameter refers to a document, validate that it still exists and is accessible at execution time.
Dynamic options should be bounded and responsive. A query that scans every file synchronously or requires a network connection to display any option can make Shortcuts configuration appear broken. Provide sensible suggestions from local state where possible, and return a clear no-results result when the entity is unavailable. Do not silently substitute a different item when a requested entity no longer resolves.
Separate metadata from execution state
The intent’s title, description, and parameterSummary are part of the system-facing interface. Localize them, describe the action rather than implementation details, and keep them consistent with actual behavior. If the action updates data, say whether it changes one item, a collection, or the current library. Do not claim an action completed merely because the intent type was discovered.
Use perform() to execute the action and return an IntentResult. A result can report success, failure, a value, a dialog, or content suitable for the invoking system experience. Map domain errors to useful explanations without exposing file paths, account identifiers, or private data. If a task is asynchronous and long-running, decide whether the intent should wait for completion, create a durable job, or report that the request was accepted but remains in progress. Do not conflate acceptance with completion.
If the action must open the app and navigate to a specific record, use the appropriate open-intent behavior and define the entity or destination explicitly. A background-capable action should avoid touching AppKit UI directly. A foreground-only workflow should communicate that boundary through its supported intent metadata and result behavior rather than failing unpredictably when invoked elsewhere.
Make Mac actions usable in Shortcuts
On macOS, expose supported AppIntent actions so people can place them in custom Shortcuts workflows. Do not use AppShortcutsProvider as a Mac discovery contract: Apple’s current platform guidance says App Shortcuts are not supported on macOS. On platforms that do support App Shortcuts, Apple recommends using them for a few common actions (usually two to five, with a maximum of ten); those limits and automatic-surfacing behavior are not a macOS guarantee.
For Mac workflows, give actions clear titles, parameter summaries, and stable inputs so users can find and compose them inside the Shortcuts app. Keep the app’s own UI available as a direct alternative. If the same App Intents code ships on other Apple platforms, isolate platform-specific App Shortcut declarations and test those surfaces on their actual target OS rather than assuming they behave like macOS.
An intent action is an automation entry point, not a private command channel and not proof that a user is currently authenticated. Validate authorization and current account identity inside the intent service. A parameter value that was resolved earlier may refer to data that has since been deleted or moved; re-check it at execution time.
Make actions safe outside the UI
Treat an intent as a public interface between the app and system automation. Validate all parameter values at the boundary, enforce domain invariants in the shared service layer, and avoid keeping essential logic only in a view controller. Reuse the same service from a menu command, keyboard shortcut, and App Intent so behavior does not diverge by entry point.
An intent can be invoked more than once, retried by a user, or run while a previous operation is still in progress. Give each mutation an idempotency rule. For a destructive action, require an explicit target and user confirmation policy appropriate to the consequence. For remote changes, use a server-side idempotency key and reconcile ambiguous timeouts before repeating the request.
If an intent depends on an account or protected resource, define the authentication policy and execution target intentionally. Do not try to bypass consent or silently launch a UI prompt from a context that cannot display one. Return a result indicating that user interaction is required, then resume through a supported flow.
Entity queries and change handling
Entity queries bridge app concepts into system parameter resolution. A query may support direct lookup by identifiers, suggested entities, or enumerating a small stable set. Implement only the query operations the product can support efficiently. If entity results are account-scoped, do not return stale suggestions after sign-out. Store stable IDs in shortcuts and re-check permissions when the system resolves them at execution time.
When data changes, update the system-facing representation through the APIs intended for the entity or shortcut type. Avoid maintaining a second mutable copy of user data inside the intent definition. The app database remains authoritative; App Intents provides a projection and an action boundary.
Testing and observability
Test the Mac intent from a Shortcuts workflow and the app’s own UI. Verify parameter inference, ambiguous names, no results, missing entities, account switch, cancellation, offline failure, duplicate invocation, and application launch behavior. Test an older saved workflow against a new app version so renamed parameters or entity identifiers do not break persisted workflows. If you also ship App Shortcuts on another supported platform, test those surfaces separately.
Record intent identifier, redacted parameter categories, execution target, outcome type, and duration. Avoid logging raw entity names or sensitive parameter values. Distinguish discovery from successful execution: the system can display an intent whose underlying operation later fails because data or network state changed.
Acceptance criteria include stable intent identifiers and entity IDs, understandable parameter summaries, an accurate result for each failure class, and no duplicate side effects when the same action is repeated. Make system automation a reliable first-class entry point without making it the only path to important functionality.
App Intents supplies the integration contract and discovery metadata. The app still owns shared business logic, identity validation, authorization, idempotency, lifecycle, and user-facing error recovery. Keep the public system action smaller and clearer than the internal implementation it invokes.
Related:
- How to Automate Repetitive Tasks on macOS with Shortcuts and Automator
- NSUserActivity on macOS: Handoff State, Continuation, and Recovery
Sources: