Skip to content
Back to skills

Ax App Intents

ASecurity

App Intents for Siri/Shortcuts, parameterized intents, discovery.

  • 4 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added May 27, 2026
developmentgoswiftdebugging

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add Kasempiternal/axiom-v2 --skill ax-app-intents --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ax App Intents?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ax App Intents
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kasempiternal-ax-app-intents/badge)](https://www.skillsdirectory.com/skills/kasempiternal-ax-app-intents)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ax-app-intents
description: "App Intents for Siri/Shortcuts, parameterized intents, discovery."
license: MIT
metadata:
  version: "1.0.0"
---

# App Intents & App Shortcuts

Expose app functionality to Siri, Apple Intelligence, Shortcuts, Spotlight, Action Button, Control Center, widgets, and Live Activities. Covers AppIntent, AppEntity, AppEnum, AppShortcutsProvider, parameterized phrases, IndexedEntity, assistant schemas, and discovery UI.

## Quick Patterns

### AppIntent — Define an Action

```swift
struct OrderSoupIntent: AppIntent {
    static var title: LocalizedStringResource = "Order Soup"
    static var description: IntentDescription = "Orders soup from the restaurant"
    static var openAppWhenRun: Bool = false

    @Parameter(title: "Soup")
    var soup: SoupEntity

    @Parameter(title: "Quantity")
    var quantity: Int?

    static var parameterSummary: some ParameterSummary {
        Summary("Order \(\.$quantity) \(\.$soup)")
    }

    func perform() async throws -> some IntentResult {
        guard let quantity, quantity > 0 else {
            throw $quantity.needsValue("How many soups?")
        }
        try await OrderService.shared.order(soup: soup, quantity: quantity)
        return .result(dialog: "Ordered \(quantity) \(soup.name)")
    }
}
```

### AppEntity — Represent App Content

```swift
struct SoupEntity: AppEntity {
    var id: String
    var name: String
    var price: Decimal

    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Soup"
    static var defaultQuery = SoupQuery()

    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(name)", subtitle: "$\(price)")
    }
}

struct SoupQuery: EntityQuery {
    func entities(for identifiers: [String]) async throws -> [SoupEntity] {
        try await SoupService.shared.fetch(ids: identifiers)
    }
    func suggestedEntities() async throws -> [SoupEntity] {
        try await SoupService.shared.popular(limit: 10)
    }
}
```

### AppEnum — Enumeration Parameters

```swift
enum SoupSize: String, AppEnum {
    case small, medium, large

    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Size"
    static var caseDisplayRepresentations: [SoupSize: DisplayRepresentation] = [
        .small: "Small (8 oz)", .medium: "Medium (12 oz)", .large: "Large (16 oz)"
    ]
}
```

### AppShortcutsProvider — Instant Discovery

```swift
struct MyAppShortcuts: AppShortcutsProvider {
    static var shortcutTileColor: ShortcutTileColor = .teal

    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        // Generic (asks for params)
        AppShortcut(
            intent: OrderSoupIntent(),
            phrases: ["Order soup in \(.applicationName)"],
            shortTitle: "Order Soup",
            systemImageName: "cup.and.saucer.fill"
        )
        // Parameterized (skips clarification)
        AppShortcut(
            intent: OrderSoupIntent(soup: .tomato, quantity: 1),
            phrases: ["Order tomato soup in \(.applicationName)"],
            shortTitle: "Tomato Soup",
            systemImageName: "flame.fill"
        )
    }

    // iOS 17+ — prevent false triggers
    static var negativePhrases: [NegativeAppShortcutPhrase] {
        NegativeAppShortcutPhrases {
            "Cancel soup order"
            "Stop ordering"
        }
    }
}
```

### Entity String Search

```swift
extension SoupQuery: EntityStringQuery {
    func entities(matching string: String) async throws -> [SoupEntity] {
        try await SoupService.shared.search(query: string)
    }
}
```

### Confirmation for Destructive Actions

```swift
func perform() async throws -> some IntentResult {
    try await requestConfirmation(
        result: .result(dialog: "Delete '\(task.title)'?"),
        confirmationActionName: .init(stringLiteral: "Delete")
    )
    try await TaskService.shared.delete(task: task)
    return .result(dialog: "Task deleted")
}
```

### Foreground Continuation with opensIntent

```swift
struct CreateEventIntent: AppIntent {
    static var openAppWhenRun: Bool = false

    func perform() async throws -> some IntentResult {
        let event = try await EventService.shared.create(title: title)
        return .result(
            value: EventEntity(from: event),
            opensIntent: OpenEventIntent(event: EventEntity(from: event))
        )
    }
}

struct OpenEventIntent: AppIntent {
    static var openAppWhenRun: Bool = true
    @Parameter(title: "Event") var event: EventEntity

    func perform() async throws -> some IntentResult {
        await MainActor.run { EventCoordinator.shared.show(event.id) }
        return .result()
    }
}
```

---

## Decision Tree

```
Starting App Intents integration?
├── Need action available in Siri/Shortcuts?
│   ├── Simple action, no parameters → AppIntent + AppShortcut (basic phrases)
│   ├── Action with entity parameter → AppIntent + AppEntity + EntityQuery
│   ├── Action with enum parameter → AppIntent + AppEnum
│   └── Common combos users repeat → Parameterized AppShortcut (skip clarification)
├── Need content searchable in Spotlight/Shortcuts?
│   ├── Small bounded list → EnumerableEntityQuery (allEntities)
│   ├── Large/unbounded list → EntityQuery + suggestedEntities + EntityStringQuery
│   └── Already using Spotlight → IndexedEntity (auto-generates Find action)
├── Need instant discovery (no user setup)?
│   └── AppShortcutsProvider with 3-5 core shortcuts
├── Need Apple Intelligence model integration?
│   └── Expose @Property on entities + use AttributedString for rich text
├── Need assistant schema (books/email/photos)?
│   └── Adopt pre-built schema protocol (BooksOpenBookIntent, etc.)
├── Need to prevent false Siri triggers?
│   └── NegativeAppShortcutPhrase (iOS 17+)
├── Need to promote shortcuts in UI?
│   ├── After action completion → SiriTipView
│   └── Settings/help screen → ShortcutsLink
└── Running on macOS?
    ├── Spotlight shows intents if all required params in parameterSummary
    └── Automations work automatically (no extra code)
```

---

## Anti-Patterns

### Coupling models to AppEntity

```swift
// BAD: Core model conforms to AppEntity
struct Book: AppEntity { ... }

// GOOD: Separate entity wrapping model
struct BookEntity: AppEntity {
    init(from book: Book) { self.id = book.id; self.title = book.title }
}
```

### Returning all entities as suggestions

```swift
// BAD: Could be thousands
func suggestedEntities() async throws -> [TaskEntity] {
    try await TaskService.shared.allTasks()
}

// GOOD: Recent/relevant subset
func suggestedEntities() async throws -> [TaskEntity] {
    try await TaskService.shared.recentTasks(limit: 10)
}
```

### Using String instead of AttributedString for model output

```swift
// BAD: Loses rich text from Apple Intelligence
@Parameter(title: "Content") var content: String

// GOOD: Preserves bold, lists, tables
@Parameter(title: "Content") var content: AttributedString
```

### Too many App Shortcuts

```swift
// BAD: Combinatorial explosion
for size in CoffeeSize.allCases {
    for type in CoffeeType.allCases {
        AppShortcut(intent: OrderIntent(type: type, size: size), ...)
    }
}

// GOOD: Generic + 2-3 common parameterized cases
AppShortcut(intent: OrderIntent(), ...)           // Generic
AppShortcut(intent: OrderIntent(type: .latte), ...)  // Common case
```

### Accessing MainActor from background intent

```swift
// BAD: Crash when openAppWhenRun = false
func perform() async throws -> some IntentResult {
    UIApplication.shared.open(url) // MainActor only!
}

// GOOD: Wrap in MainActor or use opensIntent
func perform() async throws -> some IntentResult {
    await MainActor.run { UIApplication.shared.open(url) }
    return .result()
}
```

### Missing parameterSummary required params (macOS Spotlight)

```swift
// BAD: Won't appear in Spotlight — notes is required but not in summary
@Parameter(title: "Notes") var notes: String
static var parameterSummary: some ParameterSummary {
    Summary("Create '\(\.$title)'")  // Missing notes!
}

// GOOD: Make optional, provide default, or include in summary
@Parameter(title: "Notes") var notes: String?  // Option 1: optional
@Parameter(title: "Notes") var notes: String = ""  // Option 2: default
```

### Long complex Siri phrases

```swift
// BAD: Too wordy, users won't remember
"I would like to order a coffee from \(.applicationName) please"

// GOOD: 3-6 words, verb-first
"Order coffee in \(.applicationName)"
```

---

## Deep Patterns

### IndexedEntity — Auto-Generated Find Actions

Adopt `IndexedEntity` to auto-generate Find actions from Spotlight integration. Maps `@Property` to Spotlight attributes via indexing keys.

```swift
struct EventEntity: AppEntity, IndexedEntity {
    var id: UUID

    @Property(title: "Title", indexingKey: \.eventTitle)
    var title: String

    @Property(title: "Start Date", indexingKey: \.startDate)
    var startDate: Date

    @Property(title: "Notes", customIndexingKey: "eventNotes")
    var notes: String?

    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Event"

    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(title)", subtitle: "\(startDate.formatted())")
    }
}
// Users get: "Find Events where Title contains 'Team' and Start Date is today"
```

**Standard indexing keys**: `\.eventTitle`, `\.startDate`, `\.endDate`, `\.eventLocation`
**Custom keys**: `customIndexingKey: "myCustomAttribute"` for non-standard attributes

### Authentication Policies

```swift
static var authenticationPolicy: IntentAuthenticationPolicy = .alwaysAllowed           // Public info
static var authenticationPolicy: IntentAuthenticationPolicy = .requiresAuthentication   // Logged-in user
static var authenticationPolicy: IntentAuthenticationPolicy = .requiresLocalDeviceAuthentication  // Face ID/Touch ID
```

### Entity Property Exposure for Apple Intelligence

Models receive JSON of exposed `@Property` values + `displayRepresentation` + `typeDisplayRepresentation`. Expose all properties you want the model to reason over.

Output types from Use Model action: Text (AttributedString), Number, Boolean, Dictionary, Date, App Entities. The runtime auto-converts types when connected to If/other actions.

### Assistant Schemas

Pre-built intent protocols for common app categories:

| Schema | Use Cases |
|--------|-----------|
| BooksIntents | Navigate pages, open books, play audiobooks |
| BrowserIntents | Bookmarks, history, window management |
| CameraIntents | Capture modes, device switching |
| EmailIntents | Draft, reply, forward, archive |
| PhotosIntents | Album/asset management, editing |
| PresentationsIntents | Slide creation, media, playback |
| SpreadsheetsIntents | Sheet management, content |
| DocumentsIntents | File management, page manipulation |

```swift
import BooksIntents

struct OpenBookIntent: BooksOpenBookIntent {
    @Parameter(title: "Book") var target: BookEntity
    func perform() async throws -> some IntentResult {
        await MainActor.run { BookReader.shared.open(book: target) }
        return .result()
    }
}
```

### PredictableIntent — Spotlight Suggestions

```swift
struct OrderCoffeeIntent: AppIntent, PredictableIntent {
    // Spotlight learns usage patterns and surfaces suggestions proactively
}
```

### Dynamic Shortcut Updates

```swift
func markAsFavorite(_ session: Session) {
    favoriteSessions.append(session)
    MeditationAppShortcuts.updateAppShortcutParameters()  // Refresh stored shortcuts
}
```

### Discovery UI Components

**SiriTipView** — show after user completes an action:

```swift
SiriTipView(intent: ReorderIntent(), isVisible: $showTip)
    .siriTipViewStyle(.dark)
```

**ShortcutsLink** — link to Shortcuts app from settings:

```swift
ShortcutsLink()  // Opens app's page in Shortcuts app
```

**ShortcutTileColor** options: `.blue`, `.grape`, `.grayBlue`, `.grayBrown`, `.grayGreen`, `.lightBlue`, `.lime`, `.navy`, `.orange`, `.pink`, `.purple`, `.red`, `.tangerine`, `.teal`, `.yellow`

### Error Handling

```swift
enum OrderError: Error, CustomLocalizedStringResourceConvertible {
    case outOfStock(itemName: String)
    case paymentFailed

    var localizedStringResource: LocalizedStringResource {
        switch self {
        case .outOfStock(let name): "Sorry, \(name) is out of stock"
        case .paymentFailed: "Payment failed. Please check your payment method"
        }
    }
}
```

---

## Diagnostics

| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| Intent not in Shortcuts app | `isDiscoverable = false` or missing | Set `isDiscoverable = true` (default) |
| Parameter won't resolve | Missing `defaultQuery` on entity | Add `static var defaultQuery = MyQuery()` |
| Intent crashes in background | Accessing MainActor without isolation | Wrap in `await MainActor.run {}` or set `openAppWhenRun = true` |
| Entity query returns empty | `entities(for:)` not implemented | Implement required EntityQuery methods |
| Shortcut not in Spotlight | Missing from AppShortcutsProvider | Add AppShortcut entry with phrases |
| Siri doesn't recognize phrase | Missing `\(.applicationName)` in phrase | Include app name interpolation |
| Siri triggers wrong app | Ambiguous phrases | Add NegativeAppShortcutPhrase (iOS 17+) |
| Not visible in macOS Spotlight | Required param missing from parameterSummary | Make optional, add default, or include in summary |
| App Shortcuts not updating | Data changed but shortcuts stale | Call `updateAppShortcutParameters()` |
| SiriTipView shows empty | Intent not in AppShortcutsProvider | Add intent to appShortcuts array |

---

## Related

- **ax-widgets** — WidgetKit interactive widgets, Live Activities
- **ax-app-intents** (this skill) covers both App Intents and App Shortcuts
- **ax-privacy** — Privacy manifests, deep link debugging, Spotlight indexing
- **ax-swiftui** — SwiftUI views for SiriTipView/ShortcutsLink integration

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.