---
name: axiom-app-shortcuts-ref
description: Use when implementing App Shortcuts for instant Siri/Spotlight availability, configuring AppShortcutsProvider, adding suggested phrases, or debugging shortcuts not appearing - covers complete App Shortcuts API for iOS 16+
license: MIT
metadata:
  version: "1.0.0"
---

# App Shortcuts Reference

## Overview

Comprehensive guide to App Shortcuts framework for making your app's actions instantly available in Siri, Spotlight, Action Button, Control Center, and other system experiences. App Shortcuts are pre-configured App Intents that work immediately after app install—no user setup required.

**Key distinction** App Intents are the actions; App Shortcuts are the pre-configured "surface" that makes those actions instantly discoverable system-wide.

---

## When to Use This Skill

Use this skill when:
- Implementing AppShortcutsProvider for your app
- Adding suggested phrases for Siri invocation
- Configuring instant Spotlight availability
- Creating parameterized shortcuts (skip Siri clarification)
- Using NegativeAppShortcutPhrase to prevent false positives (iOS 17+)
- Promoting shortcuts with SiriTipView
- Updating shortcuts dynamically with updateAppShortcutParameters()
- Debugging shortcuts not appearing in Shortcuts app or Spotlight
- Choosing between App Intents and App Shortcuts

Do NOT use this skill for:
- General App Intents implementation (use app-intents-ref)
- Core Spotlight indexing (use core-spotlight-ref)
- Overall discoverability strategy (use app-discoverability)

---

## Related Skills

- **app-intents-ref** — Complete App Intents implementation reference
- **app-discoverability** — Strategic guide for making apps discoverable
- **core-spotlight-ref** — Core Spotlight and NSUserActivity integration

---

## App Shortcuts vs App Intents

| Aspect | App Intent | App Shortcut |
|--------|-----------|--------------|
| **Discovery** | Must be found in Shortcuts app | Instantly available after install |
| **Configuration** | User configures in Shortcuts | Pre-configured by developer |
| **Siri activation** | Requires custom phrase setup | Works immediately with provided phrases |
| **Spotlight** | Requires donation or IndexedEntity | Appears automatically |
| **Action button** | Not directly accessible | Can be assigned immediately |
| **Setup time** | Minutes per user | Zero |

**When to use App Shortcuts** Every app should provide App Shortcuts for core functionality. They dramatically improve discoverability with zero user effort.

---

## Core Concepts

### AppShortcutsProvider Protocol

**Required conformance** Your app must have exactly one type conforming to `AppShortcutsProvider`.

```swift
struct MyAppShortcuts: AppShortcutsProvider {
    // Required: Define your shortcuts
    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] { get }

    // Optional: Branding color
    static var shortcutTileColor: ShortcutTileColor { get }

    // Optional: Dynamic updates
    static func updateAppShortcutParameters()

    // Optional: Negative phrases (iOS 17+)
    static var negativePhrases: [NegativeAppShortcutPhrase] { get }
}
```

**Platform support** iOS 16+, iPadOS 16+, macOS 13+, tvOS 16+, watchOS 9+

---

### AppShortcut Structure

Associates an `AppIntent` with spoken phrases and metadata.

```swift
AppShortcut(
    intent: StartMeditationIntent(),
    phrases: [
        "Start meditation in \(.applicationName)",
        "Begin mindfulness with \(.applicationName)"
    ],
    shortTitle: "Meditate",
    systemImageName: "figure.mind.and.body"
)
```

**Components:**
- `intent` — The App Intent to execute
- `phrases` — Spoken/typed phrases for Siri/Spotlight
- `shortTitle` — Short label for Shortcuts app tiles
- `systemImageName` — SF Symbol for visual representation

---

### AppShortcutPhrase (Suggested Phrases)

**String interpolation** Phrases use `\(.applicationName)` to dynamically include your app's name.

```swift
phrases: [
    "Start meditation in \(.applicationName)",
    "Meditate with \(.applicationName)"
]
```

**User sees in Siri/Spotlight:**
- "Start meditation in Calm"
- "Meditate with Calm"

**Why this matters** The system uses these exact phrases to trigger your intent via Siri and show suggestions in Spotlight.

---

### @AppShortcutsBuilder

Result builder for defining shortcuts array.

```swift
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
    AppShortcut(intent: OrderIntent(), /* ... */)
    AppShortcut(intent: ReorderIntent(), /* ... */)

    if UserDefaults.standard.bool(forKey: "premiumUser") {
        AppShortcut(intent: CustomizeIntent(), /* ... */)
    }
}
```

**Result builder features:**
- Conditional shortcuts (if/else)
- Loop-generated shortcuts (for-in)
- Inline array construction

---

## Phrase Template Patterns

### Basic Phrases (No Parameters)

```swift
AppShortcut(
    intent: StartWorkoutIntent(),
    phrases: [
        "Start workout in \(.applicationName)",
        "Begin exercise with \(.applicationName)",
        "Work out in \(.applicationName)"
    ],
    shortTitle: "Start Workout",
    systemImageName: "figure.run"
)
```

**Benefits:**
- Simple, discoverable
- Works for all users
- No parameter ambiguity

**Use when** Intent has no required parameters or parameters have defaults.

---

### Parameterized Phrases (Skip Clarification)

Pre-configure intents with specific parameter values to skip Siri's clarification step.

```swift
// Intent with parameters
struct StartMeditationIntent: AppIntent {
    static var title: LocalizedStringResource = "Start Meditation"

    @Parameter(title: "Type")
    var meditationType: MeditationType?

    @Parameter(title: "Duration")
    var duration: Int?
}

// Shortcuts with different parameter combinations
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
    // Generic version (will ask for parameters)
    AppShortcut(
        intent: StartMeditationIntent(),
        phrases: ["Start meditation in \(.applicationName)"],
        shortTitle: "Meditate",
        systemImageName: "figure.mind.and.body"
    )

    // Specific versions (skip parameter step)
    AppShortcut(
        intent: StartMeditationIntent(
            meditationType: .mindfulness,
            duration: 10
        ),
        phrases: [
            "Start quick mindfulness in \(.applicationName)",
            "10 minute mindfulness in \(.applicationName)"
        ],
        shortTitle: "Quick Mindfulness",
        systemImageName: "brain.head.profile"
    )

    AppShortcut(
        intent: StartMeditationIntent(
            meditationType: .sleep,
            duration: 20
        ),
        phrases: [
            "Start sleep meditation in \(.applicationName)"
        ],
        shortTitle: "Sleep Meditation",
        systemImageName: "moon.stars.fill"
    )
}
```

**Benefits:**
- One-phrase completion (no follow-up questions)
- Better user experience for common use cases
- Spotlight shows specific shortcuts

**Trade-off** More shortcuts = more visual clutter in Shortcuts app. Balance common cases (3-5 shortcuts) vs flexibility (generic shortcut with parameters).

---

## NegativeAppShortcutPhrase (iOS 17+)

Train the system to NOT invoke your app for certain phrases.

```swift
struct MeditationAppShortcuts: AppShortcutsProvider {
    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: StartMeditationIntent(),
            phrases: ["Start meditation in \(.applicationName)"],
            shortTitle: "Meditate",
            systemImageName: "figure.mind.and.body"
        )
    }

    // Prevent false positives
    static var negativePhrases: [NegativeAppShortcutPhrase] {
        NegativeAppShortcutPhrases {
            "Stop meditation"
            "Cancel meditation"
            "End session"
        }
    }
}
```

**When to use:**
- Phrases that sound similar to your shortcuts but mean the opposite
- Common phrases users might say that shouldn't trigger your app
- Disambiguation when multiple apps have similar capabilities

**Platform** iOS 17.0+, iPadOS 17.0+, macOS 14.0+, tvOS 17.0+, watchOS 10.0+

---

## Discovery UI Components

### SiriTipView — Promote Shortcuts In-App

Display the spoken phrase for a shortcut directly in your app's UI.

```swift
import AppIntents
import SwiftUI

struct OrderConfirmationView: View {
    @State private var showSiriTip = true

    var body: some View {
        VStack {
            Text("Order confirmed!")

            // Show Siri tip after successful order
            SiriTipView(intent: ReorderIntent(), isVisible: $showSiriTip)
                .siriTipViewStyle(.dark)
        }
    }
}
```

**Requirements:**
- Intent must be used in an AppShortcut (otherwise shows empty view)
- isVisible binding controls display state

**Styles:**
- `.automatic` — Adapts to environment
- `.light` — Light background
- `.dark` — Dark background

**Best practice** Show after users complete actions, suggesting easier ways next time.

---

### ShortcutsLink — Link to Shortcuts App

Opens your app's page in the Shortcuts app, listing all available shortcuts.

```swift
import AppIntents
import SwiftUI

struct SettingsView: View {
    var body: some View {
        List {
            Section("Siri & Shortcuts") {
                ShortcutsLink()
                // Displays "Shortcuts" with standard link styling
            }
        }
    }
}
```

**When to use:**
- Settings screen
- Help/Support section
- Onboarding flow

**Benefits** Single tap takes users to see all your app's shortcuts, with suggested phrases visible.

---

### ShortcutTileColor — Branding

Set the color for your shortcuts in the Shortcuts app.

```swift
struct CoffeeAppShortcuts: AppShortcutsProvider {
    static var shortcutTileColor: ShortcutTileColor = .tangerine

    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        // ...
    }
}
```

**Available colors:**
| Color | Use Case |
|-------|----------|
| `.blue` | Default, professional |
| `.tangerine` | Energy, food/beverage |
| `.purple` | Creative, meditation |
| `.teal` | Health, wellness |
| `.red` | Urgent, important |
| `.pink` | Lifestyle, social |
| `.navy` | Business, finance |
| `.yellow` | Productivity, notes |
| `.lime` | Fitness, outdoor |

Full list: `.blue`, `.grape`, `.grayBlue`, `.grayBrown`, `.grayGreen`, `.lightBlue`, `.lime`, `.navy`, `.orange`, `.pink`, `.purple`, `.red`, `.tangerine`, `.teal`, `.yellow`

**Choose color** that matches your app icon or brand identity.

---

## Dynamic Updates

### updateAppShortcutParameters()

Call when parameter options change to refresh stored shortcuts.

```swift
struct MeditationAppShortcuts: AppShortcutsProvider {
    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        // Shortcuts can reference dynamic data
        for session in MeditationData.favoriteSessions {
            AppShortcut(
                intent: StartSessionIntent(session: session),
                phrases: ["Start \(session.name) in \(.applicationName)"],
                shortTitle: session.name,
                systemImageName: session.iconName
            )
        }
    }

    static func updateAppShortcutParameters() {
        // Called automatically when needed
        // Override only if you need custom behavior
    }
}

// In your app, when data changes
extension MeditationData {
    func markAsFavorite(_ session: Session) {
        favoriteSessions.append(session)

        // Update App Shortcuts to reflect new data
        MeditationAppShortcuts.updateAppShortcutParameters()
    }
}
```

**When to call:**
- User adds/removes favorites
- Available options change
- App data structure updates

**Automatic invocation** The system calls this periodically, but you can force updates when you know data changed.

---

## Complete Implementation Example

### Step 1: Define App Intents

```swift
import AppIntents

struct OrderCoffeeIntent: AppIntent {
    static var title: LocalizedStringResource = "Order Coffee"
    static var description = IntentDescription("Orders coffee for pickup")

    @Parameter(title: "Coffee Type")
    var coffeeType: CoffeeType

    @Parameter(title: "Size")
    var size: CoffeeSize

    @Parameter(title: "Customizations")
    var customizations: String?

    static var parameterSummary: some ParameterSummary {
        Summary("Order \(\.$size) \(\.$coffeeType)") {
            \.$customizations
        }
    }

    func perform() async throws -> some IntentResult {
        let order = try await CoffeeService.shared.order(
            type: coffeeType,
            size: size,
            customizations: customizations
        )

        return .result(
            value: order,
            dialog: "Your \(size) \(coffeeType) is ordered for pickup"
        )
    }
}

struct ReorderLastIntent: AppIntent {
    static var title: LocalizedStringResource = "Reorder Last Coffee"
    static var description = IntentDescription("Reorders your most recent coffee")
    static var openAppWhenRun: Bool = false

    func perform() async throws -> some IntentResult {
        guard let lastOrder = try await CoffeeService.shared.lastOrder() else {
            throw CoffeeError.noRecentOrders
        }

        try await CoffeeService.shared.reorder(lastOrder)

        return .result(
            dialog: "Reordering your \(lastOrder.coffeeName)"
        )
    }
}

enum CoffeeType: String, AppEnum {
    case latte, cappuccino, americano, espresso

    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Coffee"
    static var caseDisplayRepresentations: [CoffeeType: DisplayRepresentation] = [
        .latte: "Latte",
        .cappuccino: "Cappuccino",
        .americano: "Americano",
        .espresso: "Espresso"
    ]
}

enum CoffeeSize: String, AppEnum {
    case small, medium, large

    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Size"
    static var caseDisplayRepresentations: [CoffeeSize: DisplayRepresentation] = [
        .small: "Small",
        .medium: "Medium",
        .large: "Large"
    ]
}
```

---

### Step 2: Create AppShortcutsProvider

```swift
import AppIntents

struct CoffeeAppShortcuts: AppShortcutsProvider {

    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        // Generic order (will ask for parameters)
        AppShortcut(
            intent: OrderCoffeeIntent(),
            phrases: [
                "Order coffee in \(.applicationName)",
                "Get coffee from \(.applicationName)"
            ],
            shortTitle: "Order",
            systemImageName: "cup.and.saucer.fill"
        )

        // Common specific orders (skip parameter step)
        AppShortcut(
            intent: OrderCoffeeIntent(
                coffeeType: .latte,
                size: .medium
            ),
            phrases: [
                "Order my usual from \(.applicationName)",
                "Get my regular coffee from \(.applicationName)"
            ],
            shortTitle: "Usual Order",
            systemImageName: "star.fill"
        )

        // Reorder last
        AppShortcut(
            intent: ReorderLastIntent(),
            phrases: [
                "Reorder coffee from \(.applicationName)",
                "Order again from \(.applicationName)"
            ],
            shortTitle: "Reorder",
            systemImageName: "arrow.clockwise"
        )
    }

    // Branding
    static var shortcutTileColor: ShortcutTileColor = .tangerine

    // Prevent false positives (iOS 17+)
    static var negativePhrases: [NegativeAppShortcutPhrase] {
        NegativeAppShortcutPhrases {
            "Cancel coffee order"
            "Stop coffee"
        }
    }
}
```

---

### Step 3: Promote in UI

```swift
import SwiftUI
import AppIntents

struct OrderConfirmationView: View {
    @State private var showReorderTip = true

    var body: some View {
        VStack(spacing: 20) {
            Image(systemName: "checkmark.circle.fill")
                .font(.system(size: 60))
                .foregroundColor(.green)

            Text("Order Placed!")
                .font(.title)

            Text("Your coffee will be ready in 10 minutes")
                .foregroundColor(.secondary)

            // Promote reorder shortcut
            if showReorderTip {
                SiriTipView(intent: ReorderLastIntent(), isVisible: $showReorderTip)
                    .siriTipViewStyle(.dark)
                    .padding(.top)
            }

            // Link to see all shortcuts
            Section {
                ShortcutsLink()
            } header: {
                Text("See all available shortcuts")
                    .font(.caption)
            }
        }
        .padding()
    }
}
```

---

## Where App Shortcuts Appear

Once implemented, your App Shortcuts are available in:

| Location | User Experience |
|----------|-----------------|
| **Siri** | Voice activation with provided phrases |
| **Spotlight** | Search for action or phrase → Instant execution |
| **Shortcuts app** | Pre-populated shortcuts, zero configuration |
| **Action Button** (iPhone 15 Pro) | Assignable to hardware button |
| **Apple Watch Ultra** | Action Button assignment |
| **Control Center** | Add shortcuts as controls |
| **Lock Screen widgets** | Quick actions without unlocking |
| **Apple Pencil Pro** | Squeeze gesture assignment |
| **Focus Filters** | Contextual filtering |

**Instant availability** All locations work immediately after app install. No user setup required.

---

## Testing & Debugging

### Verify Shortcuts Appear in Shortcuts App

1. Build and run your app on device
2. Open Shortcuts app
3. Tap "+" to create new shortcut
4. Search for your app name
5. Verify shortcuts appear with correct titles and icons

**If shortcuts don't appear:**
- Ensure AppShortcutsProvider is in your main app target
- Check that `isDiscoverable` is true for the AppIntents (default)
- Rebuild and reinstall app
- Check console for AppShortcuts errors

---

### Test Siri Invocation

1. Invoke Siri
2. Say one of your suggested phrases
3. Verify Siri executes the intent

**Example**:
- You: "Order coffee in CoffeeApp"
- Siri: "What size and type?"
- You: "Medium latte"
- Siri: "Your medium latte is ordered for pickup"

**If Siri doesn't recognize phrase:**
- Check phrase includes `\(.applicationName)`
- Verify phrase is in appShortcuts array
- Try simpler phrases (3-6 words ideal)
- Avoid complex grammar or rare words

---

### Test Spotlight Discovery

1. Swipe down to open Spotlight
2. Type your app name or shortcut phrase
3. Verify shortcut appears in results
4. Tap to execute

**If shortcut doesn't appear in Spotlight:**
- Wait a few minutes (indexing delay)
- Restart device
- Check System Settings → Siri & Search → [Your App] → Show App in Search

---

### Debug with Console Logs

```swift
#if DEBUG
struct CoffeeAppShortcuts: AppShortcutsProvider {
    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        let shortcuts = [
            AppShortcut(/* ... */),
            // ...
        ]

        print("📱 Registered \(shortcuts.count) App Shortcuts")
        shortcuts.forEach { shortcut in
            print("  - \(shortcut.shortTitle)")
        }

        return shortcuts
    }
}
#endif
```

**Check Xcode console** after app launch to verify shortcuts are registered.

---

## Best Practices

### 1. Phrase Design

#### ❌ DON'T: Long, complex phrases
```swift
phrases: [
    "I would like to order a coffee from \(.applicationName) please"
]
```

#### ✅ DO: Short, natural phrases
```swift
phrases: [
    "Order coffee in \(.applicationName)",
    "Get coffee from \(.applicationName)"
]
```

**Guidelines:**
- 3-6 words ideal
- Start with verb (Order, Start, Get, Show)
- Include `\(.applicationName)` for disambiguation
- Use natural language users would actually say

---

### 2. Shortcut Quantity

#### ❌ DON'T: Provide 20+ shortcuts
```swift
// Bad: Overwhelming
AppShortcut for every possible combination
```

#### ✅ DO: Focus on 3-5 core actions
```swift
// Good: Focused on common tasks
AppShortcut(intent: OrderIntent(), /* ... */)
AppShortcut(intent: ReorderIntent(), /* ... */)
AppShortcut(intent: ViewOrdersIntent(), /* ... */)
```

**Why** Too many shortcuts creates clutter. Focus on high-value, frequently-used actions.

---

### 3. Parameter Combinations

#### ❌ DON'T: Parameterize every variant
```swift
// Bad: Creates 12 shortcuts (3 sizes × 4 types)
for size in CoffeeSize.allCases {
    for type in CoffeeType.allCases {
        AppShortcut(intent: OrderIntent(type: type, size: size), /* ... */)
    }
}
```

#### ✅ DO: Provide generic + top 2-3 common cases
```swift
// Good: Generic + common specific cases
AppShortcut(intent: OrderIntent(), /* ... */)  // Generic
AppShortcut(intent: OrderIntent(type: .latte, size: .medium), /* ... */)  // Usual
AppShortcut(intent: OrderIntent(type: .espresso, size: .small), /* ... */)  // Quick
```

---

### 4. Short Titles

#### ❌ DON'T: Verbose or redundant
```swift
shortTitle: "Order Coffee from Coffee App"
```

#### ✅ DO: Concise and clear
```swift
shortTitle: "Order"
```

**Context** App name already appears in Shortcuts app, so no need to repeat.

---

### 5. System Images

#### ❌ DON'T: Use custom images
```swift
// Not supported
shortImage: UIImage(named: "custom")
```

#### ✅ DO: Use SF Symbols
```swift
systemImageName: "cup.and.saucer.fill"
```

**Why** SF Symbols scale properly, support dark mode, and integrate with system UI.

---

## Resources

**WWDC**: 2022-10170, 2022-10169, 260

**Docs**: /appintents/appshortcutsprovider, /appintents/appshortcut, /appintents/app-shortcuts

**Skills**: axiom-app-intents-ref, axiom-app-discoverability, axiom-core-spotlight-ref

---

**Remember** App Shortcuts make your app's functionality instantly available across iOS. Define 3-5 core shortcuts with natural phrases, promote them in your UI with SiriTipView, and users can invoke them immediately via Siri, Spotlight, Action Button, and more.
