
Ios App Intents
- 55 installs
- 4.9k repo stars
- Updated July 14, 2026
- openai/plugins
ios-app-intents is an agent skill for designing App Intents, entities, and App Shortcuts that expose iOS actions to system surfaces.
About
The ios-app-intents skill designs a minimal App Intents layer that exposes high-value iOS actions to Shortcuts, Siri, Spotlight, widgets, and controls. It starts from one to three verbs people want outside the app such as compose, open, find, filter, continue, inspect, or start rather than mirroring full navigation trees. AppEntity types stay narrower than persistence models with EntityQuery only where disambiguation helps. Each action chooses inline completion versus openAppWhenRun handoff into a predictable main-scene routing path instead of scattered side effects. AppShortcutsProvider entries use direct task-oriented phrases and symbols aligned across widgets and controls sharing parameters. The workflow validates compile success, runtime routing, and summarizes exposed actions, backing entities, and invocation handling. Strong defaults favor a dedicated intents target, thin intent types with domain logic in services, small display-friendly entities, AppEnum for fixed choices, and treating App Intents as system integration infrastructure. Anti-patterns include screen-per-intent taxonomies, full model graphs as entities, and vague shortcut phrases.
- Starts from one to three high-value actions instead of mirroring every screen.
- Defines narrow AppEntity types with optional EntityQuery disambiguation.
- Chooses inline completion versus openAppWhenRun with clear runtime handoff.
- Adds AppShortcutsProvider phrases for Shortcuts, Siri, and Spotlight discovery.
- Keeps intent types thin with business logic in app services or domain models.
Ios App Intents by the numbers
- 55 all-time installs (skills.sh)
- +3 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #587 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
ios-app-intents capabilities & compatibility
- Capabilities
- high value action selection for system surfaces · appentity and entityquery design · inline versus open app intent routing decisions · appshortcutsprovider discovery configuration · runtime handoff validation and exposure summary
- Use cases
- frontend · orchestration
What ios-app-intents says it does
Expose the smallest useful action and entity surface to the system.
Identify the 1-3 highest-value actions that should work outside the app UI.
Treat App Intents as system integration infrastructure, not only as a Shortcuts feature.
npx skills add https://github.com/openai/plugins --skill ios-app-intentsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 55 |
|---|---|
| repo stars | ★ 4.9k |
| Last updated | July 14, 2026 |
| Repository | openai/plugins ↗ |
How do I expose the right iOS app actions to Siri, Shortcuts, Spotlight, and widgets without over-building intents?
Design App Intents, AppEntity types, and App Shortcuts exposing high-value iOS actions to Siri, Spotlight, widgets, and controls.
Who is it for?
iOS developers adding App Intents for Shortcuts, Siri, Spotlight, widgets, or controls.
Skip if: Skip for Android integrations, full app navigation redesign, or projects without App Intents targets.
When should I use this skill?
User asks to add App Intents, App Shortcuts, Siri actions, or system integrations for an iOS app.
What you get
A small intents surface with entities, shortcuts, validated routing, and a summary of exposed system actions.
Files
iOS App Intents
Overview
Expose the smallest useful action and entity surface to the system. Start with the verbs and objects people would actually want outside the app, then implement a narrow App Intents layer that can deep-link or hand off cleanly into the main app when needed.
Read these references as needed:
references/first-pass-checklist.mdfor choosing the first intent and entity surfacereferences/example-patterns.mdfor concrete example shapes to copy and adaptreferences/code-templates.mdfor generalized App Intents code templatesreferences/system-surfaces.mdfor how to think about Shortcuts, Siri, Spotlight, widgets, and other system entry points
Core workflow
1) Start with actions, not screens
- Identify the 1-3 highest-value actions that should work outside the app UI.
- Prefer verbs like compose, open, find, filter, continue, inspect, or start.
- Do not mirror the entire app navigation tree as intents.
2) Define a small entity surface
- Add
AppEntitytypes only for the objects the system needs to understand or route. - Keep the entity shape narrower than the app's persistence model.
- Add
EntityQueryor other query types only where disambiguation or suggestions are genuinely useful.
3) Decide whether the action completes in place or opens the app
- Use non-opening intents for actions that can complete directly from the system surface.
- Use
openAppWhenRunor open-style intents when the user should land in a specific in-app workflow. - When the app must react inside the main scene, add one clear runtime handoff path instead of scattering ad hoc routing logic.
- If the action can work in both modes, consider shipping both an inline version and an open-app version rather than forcing one compromise.
4) Make the actions discoverable
- Add
AppShortcutsProviderentries for the first set of high-value intents. - Choose titles, phrases, and symbols that make sense in Shortcuts, Siri, and Spotlight.
- Keep shortcut phrases direct and task-oriented.
- Reuse the same action model for widgets and controls when a widget configuration or intent-driven control already needs the same parameters.
5) Validate the runtime handoff
- Build the app and confirm the intents target compiles cleanly.
- Verify the app opens or routes to the expected place when an intent runs.
- Summarize which actions are now exposed, which entities back them, and how the app handles invocation.
Strong defaults
- Prefer a dedicated intents target or module for the system-facing layer.
- Keep intent types thin; business logic should stay in app services or domain models.
- Keep app entities small and display-friendly.
- Use
AppEnumfor fixed app choices such as tabs, modes, or visibility levels before reaching for a full entity type. - Prefer one predictable app-intent routing surface in the main app scene or root router.
- Treat App Intents as system integration infrastructure, not only as a Shortcuts feature.
Anti-patterns
- Exposing every screen or tab as its own intent without a real user value.
- Mirroring the entire model graph as
AppEntitytypes. - Hiding runtime handoff in global side effects with no clear app entry path.
- Adding App Shortcuts with vague phrases or generic titles.
- Treating the first App Intents pass as a broad taxonomy project instead of a small useful release.
Notes
- Apple documentation to use as primary references:
https://developer.apple.com/documentation/appintents/making-actions-and-content-discoverable-and-widely-availablehttps://developer.apple.com/documentation/appintents/creating-your-first-app-intenthttps://developer.apple.com/documentation/appintents/adopting-app-intents-to-support-system-experiences- In addition to the links above, use web search to consult current Apple Developer documentation when App Intents APIs or platform behavior may have changed.
- A good first pass often includes one open-app intent, one action intent, one or two entity types, and a small
AppShortcutsProvider. - Good example families to cover are:
- open a destination or editor in the app
- perform a lightweight action inline without opening the app
- choose from a fixed enum such as a tab or mode
- resolve one or more entities through
EntityQuery - power widget configuration or controls from the same entity surface
interface:
display_name: "iOS App Intents"
short_description: "Build and debug iOS App Intents integrations"
Code templates
These templates are intentionally generic. Rename types and services to fit the app.
Open-app handoff intent
import AppIntents
struct OpenComposerIntent: AppIntent {
static let title: LocalizedStringResource = "Open composer"
static let description = IntentDescription("Open the app to compose content")
static let openAppWhenRun = true
@Parameter(
title: "Prefilled text",
inputConnectionBehavior: .connectToPreviousIntentResult
)
var text: String?
func perform() async throws -> some IntentResult {
await MainActor.run {
AppIntentRouter.shared.handledIntent = .init(intent: self)
}
return .result()
}
}Scene-side handoff:
import AppIntents
import Observation
@Observable
final class AppIntentRouter {
struct HandledIntent: Equatable {
let id = UUID()
let intent: any AppIntent
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.id == rhs.id
}
}
static let shared = AppIntentRouter()
var handledIntent: HandledIntent?
private init() {}
}
private func handleIntent() {
guard let intent = appIntentRouter.handledIntent?.intent else { return }
if let composerIntent = intent as? OpenComposerIntent {
appRouter.presentComposer(prefilledText: composerIntent.text ?? "")
} else if let sectionIntent = intent as? OpenSectionIntent {
selectedTab = sectionIntent.section.toTab
}
}Inline action intent
import AppIntents
struct CreateItemIntent: AppIntent {
static let title: LocalizedStringResource = "Create item"
static let description = IntentDescription("Create a new item without opening the app")
static let openAppWhenRun = false
@Parameter(title: "Title")
var title: String
@Parameter(title: "Workspace")
var workspace: WorkspaceEntity
func perform() async throws -> some IntentResult & ProvidesDialog {
do {
try await ItemService.shared.createItem(title: title, workspaceID: workspace.id)
return .result(dialog: "Created \(title).")
} catch {
return .result(dialog: "Could not create the item. Please try again.")
}
}
}Fixed selection with AppEnum
import AppIntents
enum SectionIntentValue: String, AppEnum {
case inbox
case projects
case settings
static var typeDisplayName: LocalizedStringResource { "Section" }
static let typeDisplayRepresentation: TypeDisplayRepresentation = "Section"
static var caseDisplayRepresentations: [Self: DisplayRepresentation] {
[
.inbox: "Inbox",
.projects: "Projects",
.settings: "Settings",
]
}
var toTab: AppTab {
switch self {
case .inbox: .inbox
case .projects: .projects
case .settings: .settings
}
}
}
struct OpenSectionIntent: AppIntent {
static let title: LocalizedStringResource = "Open section"
static let openAppWhenRun = true
@Parameter(title: "Section")
var section: SectionIntentValue
func perform() async throws -> some IntentResult {
await MainActor.run {
AppIntentRouter.shared.handledIntent = .init(intent: self)
}
return .result()
}
}Entity and query
import AppIntents
struct WorkspaceEntity: AppEntity, Identifiable {
let workspace: Workspace
var id: String { workspace.id }
static let typeDisplayRepresentation: TypeDisplayRepresentation = "Workspace"
static let defaultQuery = WorkspaceQuery()
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(workspace.name)")
}
}
struct WorkspaceQuery: EntityQuery {
func entities(for identifiers: [WorkspaceEntity.ID]) async throws -> [WorkspaceEntity] {
let workspaces = try await WorkspaceStore.shared.workspaces(matching: identifiers)
return workspaces.map(WorkspaceEntity.init)
}
func suggestedEntities() async throws -> [WorkspaceEntity] {
try await WorkspaceStore.shared.recentWorkspaces().map(WorkspaceEntity.init)
}
func defaultResult() async -> WorkspaceEntity? {
guard let workspace = try? await WorkspaceStore.shared.currentWorkspace() else { return nil }
return WorkspaceEntity(workspace: workspace)
}
}Dependent query
import AppIntents
struct ProjectEntity: AppEntity, Identifiable {
let project: Project
var id: String { project.id }
static let typeDisplayRepresentation: TypeDisplayRepresentation = "Project"
static let defaultQuery = ProjectQuery()
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(project.name)")
}
}
struct ProjectSelectionIntent: WidgetConfigurationIntent {
static let title: LocalizedStringResource = "Project widget configuration"
@Parameter(title: "Workspace")
var workspace: WorkspaceEntity?
@Parameter(title: "Project")
var project: ProjectEntity?
}
struct ProjectQuery: EntityQuery {
@IntentParameterDependency<ProjectSelectionIntent>(\.$workspace)
var workspace
func entities(for identifiers: [ProjectEntity.ID]) async throws -> [ProjectEntity] {
try await fetchProjects().filter { identifiers.contains($0.id) }.map(ProjectEntity.init)
}
func suggestedEntities() async throws -> [ProjectEntity] {
try await fetchProjects().map(ProjectEntity.init)
}
func defaultResult() async -> ProjectEntity? {
try? await fetchProjects().first.map(ProjectEntity.init)
}
private func fetchProjects() async throws -> [Project] {
guard let workspaceID = workspace?.id else { return [] }
return try await ProjectStore.shared.projects(in: workspaceID)
}
}Widget configuration intent
import AppIntents
import WidgetKit
struct ActivityWidgetConfiguration: WidgetConfigurationIntent {
static let title: LocalizedStringResource = "Activity widget configuration"
static let description = IntentDescription("Choose which workspace and filter the widget should show")
@Parameter(title: "Workspace")
var workspace: WorkspaceEntity?
@Parameter(title: "Filter")
var filter: ActivityFilterEntity?
}App shortcuts provider
import AppIntents
struct AppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: OpenComposerIntent(),
phrases: [
"Open composer in \(.applicationName)",
"Draft with \(.applicationName)",
],
shortTitle: "Open composer",
systemImageName: "square.and.pencil"
)
AppShortcut(
intent: CreateItemIntent(),
phrases: [
"Create item with \(.applicationName)",
"Add a task in \(.applicationName)",
],
shortTitle: "Create item",
systemImageName: "plus.circle"
)
}
}Inline file input
import AppIntents
import UniformTypeIdentifiers
struct ImportAttachmentIntent: AppIntent {
static let title: LocalizedStringResource = "Import attachment"
static let openAppWhenRun = false
@Parameter(
title: "Files",
supportedContentTypes: [.image, .pdf, .plainText],
inputConnectionBehavior: .connectToPreviousIntentResult
)
var files: [IntentFile]
func perform() async throws -> some IntentResult & ProvidesDialog {
guard !files.isEmpty else {
return .result(dialog: "No files were provided.")
}
for file in files {
guard let url = file.fileURL else { continue }
_ = url.startAccessingSecurityScopedResource()
defer { url.stopAccessingSecurityScopedResource() }
try await AttachmentImporter.shared.importFile(at: url)
}
return .result(dialog: "Imported \(files.count) file(s).")
}
}Example patterns
Use these as starting points when deciding what to expose first.
1) Open-app handoff intent
Best for:
- compose flows
- editors
- navigation to a destination
- actions that need the full app scene, auth state, or richer UI
Pattern:
openAppWhenRun = true- collect lightweight input in the intent
- store one handled-intent payload in a central router or handoff service
- let the app scene translate that payload into tabs, sheets, routes, or windows
Example:
- "Open the app to compose a draft"
- "Open the app on a selected section"
- "Open an editor prefilled with content from the previous shortcut step"
2) Inline background action intent
Best for:
- quick create/update actions
- send, archive, mark, favorite, or toggle operations
- actions that can finish without the main app UI
Pattern:
openAppWhenRun = false- perform the operation directly in
perform() - return dialog or snippet feedback so the result feels complete in Shortcuts or Siri
Example:
- "Create a task"
- "Send a message"
- "Archive a document"
3) Paired open-app and inline variants
Best for:
- actions that need both automation and richer manual review
- flows where some users want a background shortcut but others want to land in the app
Pattern:
- keep parameter names aligned between the two intents
- let the open-app version hand off to UI
- let the inline version call the same domain service directly
- expose both in
AppShortcutsProviderwith clear titles
Example:
- "Draft in app" and "Send now"
- "Open image post editor" and "Post images in background"
4) Fixed choice via AppEnum
Best for:
- tabs
- modes
- visibility levels
- small sets of filters or categories
Pattern:
- define an
AppEnum - give every case a user-facing
DisplayRepresentation - map enum cases into app-specific types in one place
Example:
- open a selected tab
- run an action in "public", "private", or "team" mode
5) Entity-backed selection via AppEntity
Best for:
- accounts
- projects
- lists
- destinations
- saved searches
Pattern:
- expose only the fields needed for display and lookup
- add
suggestedEntities()for picker UX - add
defaultResult()only when there is a genuinely helpful default - keep network or database fetch logic inside the query type, not the view layer
Example:
- choose an account to post from
- pick a project to open
- select a saved list for a widget
6) Query dependency between parameters
Best for:
- when one parameter changes the valid choices for another
- widget or control configuration where "account" determines "project"
Pattern:
- use
@IntentParameterDependencyinside the query - read the upstream parameter
- scope entity fetching to the chosen parent value
Example:
- selected workspace filters available documents
- selected account filters available lists
7) Widget configuration intent
Best for:
- widgets that need a selected account, project, filter, or destination
- intent-driven controls that should reuse the same parameter model
Pattern:
- define a
WidgetConfigurationIntent - use the same
AppEntitytypes that your shortcuts already use - provide preview-friendly sample values when the widget needs them
Example:
- choose account plus list
- choose project plus status filter
8) Shortcut phrase design
Best for:
- making actions discoverable in Siri and Shortcuts
Pattern:
- keep phrases short and verb-led
- expose one or two canonical phrases, then add only a few natural variants
- use precise
shortTitleandsystemImageName
Example:
- "Create a note with \(.applicationName)"
- "Open inbox in \(.applicationName)"
- "Send image with \(.applicationName)"
First-pass checklist
Use this checklist when deciding what to expose in the first App Intents release.
Pick the first actions
Choose actions that are:
- useful without browsing the full app first
- easy to describe in one sentence
- valuable in Shortcuts, Siri, Spotlight, or widgets
- backed by existing app logic instead of requiring a major rewrite
Good first candidates:
- compose something
- open a destination or object
- find or filter a known object
- continue an existing workflow
- start a focused action
Avoid as a first pass:
- giant setup flows
- actions that only make sense after many in-app taps
- low-value screens exposed only because they exist
Pick the first entities
Use app entities when the system needs to identify or display app objects.
Good first entities:
- account
- list
- filter
- destination
- draft
- media item
Keep each entity focused on:
- identifier
- display representation
- the few fields the system needs for routing or disambiguation
Do not mirror the entire persistence model if a much smaller system-facing type will do.
Decide the handoff model
For each intent, ask:
- Can this finish directly from the system surface?
- Should this open the app to a specific place?
- If it opens the app, what is the single clean route back into the main scene?
Prefer one explicit routing or handoff service over many feature-specific side channels.
System surfaces
Think in system entry points, not just in shortcuts.
Shortcuts
- Good for direct actions and automation chains.
- Expose the actions that users would actually want to reuse.
- Add
AppShortcutsProviderentries for the first high-value intents.
Siri
- Good for clear verbs and deep-linkable actions.
- Phrase titles and parameters so the system can present and disambiguate them clearly.
Spotlight
- Good for discoverability of both actions and entities.
- Use strong display representations and clear type names.
Widgets, Live Activities, and controls
- Good when the same actions already make sense as intent-driven entry points.
- Reuse the same intent surface where practical instead of inventing separate action models.
General guidance
- Design one small action layer that can serve several surfaces.
- Keep action names concrete and user-facing.
- Prefer structured entities and parameters over trying to encode everything in free-form text.
- Start narrow, ship a useful set, then expand based on real use.
Related skills
FAQ
How many intents should ios-app-intents start with?
One to three high-value actions that users want outside the app UI, not every screen as an intent.
When should an intent open the app versus run inline?
Use inline intents when the action completes on the system surface; use openAppWhenRun when users need a specific in-app workflow.
Is ios-app-intents safe to install?
Review the Security Audits panel on this page before installing in production.