mcpField
A
axiom-app-shortcuts-ref

axiom-app-shortcuts-ref

github.com/charleswiltgen开发1.2k172 次安装今天更新
代码仓库

关于 axiom-app-shortcuts-ref

面向 Apple 平台开发者的 App Shortcuts 框架参考技能,涵盖快捷指令配置、Siri 与 Spotlight 发现及调试。重点区分 App Intents 与预配置快捷指令,并提供参数化短语等实现指导。

主要功能

  1. 01配置 AppShortcutsProvider
  2. 02设计 Siri 与 Spotlight 短语
  3. 03创建参数化快捷指令
  4. 04使用 SiriTipView 推广快捷指令
  5. 05动态更新快捷指令参数
  6. 06排查快捷指令发现问题

适用场景

  1. 01为应用核心操作添加快捷指令
  2. 02让 Siri 和 Spotlight 触发应用操作
  3. 03减少 Siri 执行时的参数追问
  4. 04排查快捷指令未显示的问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-app-shortcuts-ref

安装后在 Claude Code 中输入/axiom-app-shortcuts-ref确认已加载。

详细介绍

App 快捷指令参考

概览

全面介绍 App Shortcuts 框架,帮助你让 App 操作立即出现在 Siri、Spotlight、操作按钮、控制中心及其他系统体验中。App Shortcuts 是预先配置好的 App Intents,App 安装后即可使用,无需用户进行任何设置。

关键区别 App Intents 是操作;App Shortcuts 则是预先配置好的“入口”,可让这些操作立即在整个系统中被发现。


何时使用此技能

在以下情况下使用此技能:

  • 为你的 App 实现 AppShortcutsProvider
  • 添加用于通过 Siri 调用的建议短语
  • 配置 Spotlight 即时可用性
  • 创建带参数的快捷指令(跳过 Siri 澄清步骤)
  • 使用 NegativeAppShortcutPhrase 防止误触发(iOS 17+)
  • 使用 SiriTipView 推广快捷指令
  • 使用 updateAppShortcutParameters() 动态更新快捷指令
  • 调试快捷指令未显示在“快捷指令”App 或 Spotlight 中的问题
  • 在 App Intents 和 App Shortcuts 之间进行选择

请勿将此技能用于:

  • 一般的 App Intents 实现(使用 app-intents-ref)
  • Core Spotlight 索引(使用 core-spotlight-ref)
  • 整体可发现性策略(使用 app-discoverability)

相关技能

  • app-intents-ref — 完整的 App Intents 实现参考
  • app-discoverability — 提升 App 可发现性的策略指南
  • core-spotlight-ref — Core Spotlight 和 NSUserActivity 集成

App Shortcuts 与 App Intents

方面App IntentApp Shortcut
发现方式必须在“快捷指令”App 中找到安装后立即可用
配置方式用户在“快捷指令”中配置由开发者预先配置
Siri 激活需要设置自定义短语使用提供的短语即可立即运行
Spotlight需要 donation 或 IndexedEntity自动显示
操作按钮无法直接访问可立即分配
设置时间每位用户需几分钟无需设置

何时使用 App Shortcuts 每个 App 都应为核心功能提供 App Shortcuts。它们无需用户付出任何努力,就能大幅提升可发现性。


核心概念

AppShortcutsProvider 协议

必须遵循 你的 App 必须恰好有一个类型遵循 AppShortcutsProvider。

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 }
}

平台支持 iOS 16+、iPadOS 16+、macOS 13+、tvOS 16+、watchOS 9+


AppShortcut 结构

将一个 AppIntent 与语音短语及元数据关联起来。

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

组成部分:

  • intent — 要执行的 App Intent
  • phrases — 用于 Siri/Spotlight 的语音/输入短语
  • shortTitle — “快捷指令”App 磁贴的短标签
  • systemImageName — 用于视觉呈现的 SF Symbol

AppShortcutPhrase(建议短语)

字符串插值 短语使用 \(.applicationName) 动态包含你的 App 名称。

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

用户在 Siri/Spotlight 中看到:

  • “在 Calm 中开始冥想”
  • “使用 Calm 冥想”

为何重要 系统会使用这些确切短语,通过 Siri 触发你的意图,并在 Spotlight 中显示建议。


@AppShortcutsBuilder

用于定义快捷指令数组的结果构建器。

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

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

结果构建器功能:

  • 条件快捷指令(if/else)
  • 循环生成快捷指令(for-in)
  • 内联数组构造

短语模板模式

基本短语(无参数)
AppShortcut(
    intent: StartWorkoutIntent(),
    phrases: [
        "Start workout in \(.applicationName)",
        "Begin exercise with \(.applicationName)",
        "Work out in \(.applicationName)"
    ],
    shortTitle: "Start Workout",
    systemImageName: "figure.run"
)

优势:

  • 简单易发现
  • 适用于所有用户
  • 不存在参数歧义

适用情况 意图没有必需参数,或参数具有默认值。


参数化短语(跳过澄清)

预先配置带有特定参数值的意图,以跳过 Siri 的澄清步骤。

// 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"
    )
}

优点:

  • 一句话即可完成(无需后续提问)
  • 为常见使用场景带来更好的用户体验
  • Spotlight 会显示具体的快捷指令

**权衡:**快捷指令越多,Shortcuts 应用中的界面就越杂乱。在常见场景(3-5 个快捷指令)和灵活性(带参数的通用快捷指令)之间取得平衡。


NegativeAppShortcutPhrase(iOS 17+)

训练系统不要因某些短语而调用你的应用。

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"
        }
    }
}

适用场景:

  • 听起来与你的快捷指令相似、但意思相反的短语
  • 用户可能会说、但不应触发你的应用的常见短语
  • 多个应用具有相似功能时用于消歧

**平台:**iOS 17.0+、iPadOS 17.0+、macOS 14.0+、tvOS 17.0+、watchOS 10.0+


发现界面组件

SiriTipView — 在应用内推广快捷指令

在应用界面中直接显示快捷指令的语音短语。

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)
        }
    }
}

要求:

  • 必须在 AppShortcut 中使用该意图(否则会显示空视图)
  • isVisible 绑定控制显示状态

样式:

  • .automatic — 根据环境自动调整
  • .light — 浅色背景
  • .dark — 深色背景

**最佳实践:**在用户完成操作后显示,提示他们下次可以用更简单的方式完成操作。


ShortcutsLink — 链接到 Shortcuts 应用

打开 Shortcuts 应用中你的应用页面,其中列出了所有可用的快捷指令。

import AppIntents
import SwiftUI

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

适用场景:

  • 设置屏幕
  • 帮助/支持部分
  • 引导流程

**优点:**用户轻点一次即可查看应用中的所有快捷指令,并看到建议的短语。


ShortcutTileColor — 品牌塑造

为 Shortcuts 应用中的快捷指令设置颜色。

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

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

可用颜色:

颜色使用场景
.blue默认、专业
.tangerine活力、食品/饮料
.purple创意、冥想
.teal健康、养生
.red紧急、重要
.pink生活方式、社交
.navy商务、金融
.yellow效率、笔记
.lime健身、户外

完整列表:.blue、.grape、.grayBlue、.grayBrown、.grayGreen、.lightBlue、.lime、.navy、.orange、.pink、.purple、.red、.tangerine、.teal、.yellow

**选择颜色:**选择与应用图标或品牌形象相符的颜色。


动态更新

updateAppShortcutParameters()

在参数选项发生变化时调用,以刷新已存储的快捷指令。

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()
    }
}

调用时机:

  • 用户添加/移除收藏项
  • 可用选项发生变化
  • 应用数据结构更新

**自动调用:**系统会定期调用此方法,但当你确认数据已更改时,也可以强制更新。


完整实现示例

步骤 1:定义 App Intents
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"
    ]
}

步骤 2:创建 AppShortcutsProvider
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"
        }
    }
}

步骤 3:在界面中推广
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()
    }
}

App Shortcuts 的显示位置

实现后,你的 App Shortcuts 可在以下位置使用:

位置用户体验
Siri使用提供的短语进行语音激活
Spotlight搜索操作或短语 → 立即执行
Shortcuts 应用预先填充的快捷指令,无需配置
Action Button(iPhone 15 Pro)可分配给硬件按钮
Apple Watch Ultra分配给 Action Button
Control Center将快捷指令添加为控制项
锁屏小组件无需解锁即可执行快捷操作
Apple Pencil Pro分配挤压手势
专注模式过滤条件根据上下文进行过滤

**立即可用:**应用安装后,所有位置都会立即生效。无需用户进行设置。


测试与调试

验证快捷指令是否显示在“快捷指令”App 中
  1. 在设备上构建并运行你的 App
  2. 打开“快捷指令”App
  3. 点击“+”以创建新快捷指令
  4. 搜索你的 App 名称
  5. 验证快捷指令是否显示,并确认标题和图标正确

如果快捷指令没有显示:

  • 确保 AppShortcutsProvider 位于主 App 目标中
  • 检查 AppIntents 的 isDiscoverable 是否为 true(默认值)
  • 重新构建并重新安装 App
  • 检查控制台中是否有 AppShortcuts 错误

测试 Siri 调用
  1. 呼叫 Siri
  2. 说出一条建议短语
  3. 验证 Siri 是否执行了该意图

示例:

  • 你:“在 CoffeeApp 订一杯咖啡”
  • Siri:“要什么尺寸和类型?”
  • 你:“中杯拿铁”
  • Siri:“已为你订好一杯中杯拿铁,供你自取”

如果 Siri 无法识别短语:

  • 检查短语是否包含 \(.applicationName)
  • 确认短语位于 appShortcuts 数组中
  • 尝试更简单的短语(理想长度为 3-6 个词)
  • 避免复杂语法或生僻词

测试 Spotlight 发现
  1. 向下轻扫以打开 Spotlight
  2. 输入你的 App 名称或快捷指令短语
  3. 验证快捷指令是否出现在结果中
  4. 轻点以执行

如果快捷指令没有出现在 Spotlight 中:

  • 等待几分钟(索引可能需要时间)
  • 重启设备
  • 检查“系统设置” →“Siri 与搜索”→ [你的 App] →“在搜索中显示 App”

使用控制台日志调试
#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

App 启动后检查 Xcode 控制台,确认快捷指令已注册。


最佳实践

1. 短语设计
❌ 不要:使用冗长、复杂的短语
phrases: [
    "I would like to order a coffee from \(.applicationName) please"
]
✅ 应该:使用简短、自然的短语
phrases: [
    "Order coffee in \(.applicationName)",
    "Get coffee from \(.applicationName)"
]

指南:

  • 理想长度为 3-6 个词
  • 以动词开头(订购、开始、获取、显示)
  • 加入 \(.applicationName) 以消除歧义
  • 使用用户实际会说的自然语言

2. 快捷指令数量
❌ 不要:提供 20 个以上的快捷指令
// Bad: Overwhelming
AppShortcut for every possible combination
✅ 应该:专注于 3-5 个核心操作
// Good: Focused on common tasks
AppShortcut(intent: OrderIntent(), /* ... */)
AppShortcut(intent: ReorderIntent(), /* ... */)
AppShortcut(intent: ViewOrdersIntent(), /* ... */)

原因 快捷指令太多会显得杂乱。应专注于价值高、使用频繁的操作。


3. 参数组合
❌ 不要:为每种变体都设置参数
// 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), /* ... */)
    }
}
✅ 应该:提供通用选项以及 2-3 个最常见的选项
// 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. 简短标题
❌ 不要:使用冗长或重复的标题
shortTitle: "Order Coffee from Coffee App"
✅ 应该:简洁明了
shortTitle: "Order"

说明 “快捷指令”App 中已经显示了 App 名称,因此无需重复。


5. 系统图像
❌ 不要:使用自定义图像
// Not supported
shortImage: UIImage(named: "custom")
✅ 应该:使用 SF Symbols
systemImageName: "cup.and.saucer.fill"

原因 SF Symbols 会正确缩放、支持深色模式,并能与系统 UI 集成。


资源

WWDC:2022-10170、2022-10169、260

文档:/appintents/appshortcutsprovider、/appintents/appshortcut、/appintents/app-shortcuts

技能:axiom-app-intents-ref、axiom-app-discoverability、axiom-core-spotlight-ref


请记住 App Shortcuts 能让用户在 iOS 中即时使用 App 的功能。定义 3-5 个带有自然短语的核心快捷指令,在 UI 中通过 SiriTipView 推广它们,用户便能立即通过 Siri、Spotlight、操作按钮等方式调用。

评论

支持 Markdown