关于 axiom-app-discoverability
面向 iOS 开发者的应用可发现性规划与排查技能,说明如何结合 App Intents、App Shortcuts、Core Spotlight 和 NSUserActivity,让操作与内容出现在系统搜索和建议中。
主要功能
- 01规划多 API 可发现性策略
- 02通过 App Intents 暴露操作
- 03配置 App Shortcuts 与建议短语
- 04用 Core Spotlight 索引内容
- 05借助 NSUserActivity 标记页面
- 06检查 Spotlight 索引问题
适用场景
- 01让应用操作出现在 Siri 和 Spotlight
- 02使应用内容可通过系统搜索找到
- 03为重要页面启用搜索与预测
- 04排查 Spotlight 结果缺失或过时
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-app-discoverability安装后在 Claude Code 中输入/axiom-app-discoverability确认已加载。
详细介绍
App Discoverability
概述
核心原则 通过多个 API 向系统提供元数据,让系统决定何时展示你的应用。
iOS 会根据你通过 App Intents、App Shortcuts、Core Spotlight 和 NSUserActivity 提供的元数据,在 Spotlight、Siri 建议和系统体验中展示应用。系统会从实际使用情况中学习,并提升常用操作的优先级。没有任何单一 API 足以实现这一目标——要全面提升可发现性,需要采用多 API 策略。
关键见解 iOS 会优先展示用户实际调用的快捷指令和活动。如果没有人使用某个意图,系统就会将其隐藏。提供清晰、以操作为导向的元数据,剩下的工作交给系统完成。
何时使用此技能
在以下情况下使用此技能:
- 让你的应用出现在 Spotlight 搜索结果中
- 让 Siri 在相关场景中推荐你的应用
- 将应用操作添加到操作按钮(iPhone/Apple Watch Ultra)
- 让应用内容可在整个系统中搜索
- 在实现之前规划可发现性架构
- 排查“为什么系统没有推荐我的应用?”
在以下情况下不要使用此技能:
- 你需要详细的 API 参考(使用 app-intents-ref、axiom-app-shortcuts-ref、axiom-core-spotlight-ref)
- 你正在实现特定 API(使用相应的参考技能)
- 你只想添加一个 App Intent(使用 app-intents-ref)
可发现性的 6 步策略
这是来自已在多个生产应用中实现可发现性功能的开发者的成熟策略。实现时间:一个晚上即可实现最小可行的可发现性功能。
步骤 1:添加 App Intents
App Intents 为 Spotlight 搜索、Siri 请求和快捷指令建议提供支持。没有 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
func perform() async throws -> some IntentResult {
try await CoffeeService.shared.order(type: coffeeType, size: size)
return .result(dialog: "Your \(size) \(coffeeType) is ordered")
}
}
为何重要 App Intents 是基础。其他功能都构建于其上。
参见:app-intents-ref,了解完整的 API 参考
步骤 2:添加带建议短语的 App Shortcuts
App Shortcuts 让你的意图在安装后立即可用。无需配置。
struct CoffeeAppShortcuts: AppShortcutsProvider {
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: OrderCoffeeIntent(),
phrases: [
"Order coffee in \(.applicationName)",
"Get my usual coffee from \(.applicationName)"
],
shortTitle: "Order Coffee",
systemImageName: "cup.and.saucer.fill"
)
}
static var shortcutTileColor: ShortcutTileColor = .tangerine
}
为何重要 没有 App Shortcuts,用户必须手动配置快捷指令。有了它们,你的操作会立即出现在 Siri、Spotlight、操作按钮和控制中心中。
重要 使用 suggestedPhrase 模式——这会提高系统在 Spotlight 操作建议和 Siri 轮播中推荐这些操作的几率。
参见:app-shortcuts-ref,了解短语模式和最佳实践
步骤 3:通过 Core Spotlight 提供可搜索的内容
为重要内容建立索引。系统会展示与用户查询匹配的项目。
import CoreSpotlight
import UniformTypeIdentifiers
func indexOrder(_ order: Order) {
let attributes = CSSearchableItemAttributeSet(contentType: .item)
attributes.title = order.coffeeName
attributes.contentDescription = "Order from \(order.date.formatted())"
attributes.keywords = ["coffee", "order", order.coffeeName]
let item = CSSearchableItem(
uniqueIdentifier: order.id.uuidString,
domainIdentifier: "orders",
attributeSet: attributes
)
CSSearchableIndex.default().indexSearchableItems([item]) { error in
if let error = error {
print("Indexing error: \(error)")
}
}
}
为何重要 Core Spotlight 让你的应用内容可供搜索。当用户在 Spotlight 中搜索“拿铁”时,你应用中的订单就会显示出来。
只为重要内容建立索引 不要为所有内容建立索引。重点关注面向用户的内容(订单、文档、笔记等)。
参见:core-spotlight-ref,了解批量处理、删除模式和最佳实践
步骤 4:为重要页面使用 NSUserActivity
将重要页面标记为符合搜索和预测条件。
func viewOrder(_ order: Order) {
let activity = NSUserActivity(activityType: "com.coffeeapp.viewOrder")
activity.title = order.coffeeName
activity.isEligibleForSearch = true
activity.isEligibleForPrediction = true
activity.persistentIdentifier = order.id.uuidString
// Connect to App Intents
activity.appEntityIdentifier = order.id.uuidString
// Provide rich metadata
let attributes = CSSearchableItemAttributeSet(contentType: .item)
attributes.contentDescription = "Your \(order.coffeeName) order"
attributes.thumbnailData = order.imageData
activity.contentAttributeSet = attributes
activity.becomeCurrent()
// In your view controller or SwiftUI view
self.userActivity = activity
}
为何重要 系统会学习用户经常访问哪些页面,并主动进行推荐。锁屏小组件、Siri 建议和 Spotlight 都能从中受益。
重要 只标记用户可能希望返回的页面。不要标记设置、引导页面或错误状态。
参见:core-spotlight-ref,了解符合条件的模式和活动延续
步骤 5:提供正确的意图元数据
清晰的描述和标题至关重要,因为 Spotlight 会直接显示它们。
❌ 不要这样:笼统或含糊
static var title: LocalizedStringResource = "Do Thing"
static var description = IntentDescription("Performs action")
✅ 应该这样:具体且以操作为导向
static var title: LocalizedStringResource = "Order Coffee"
static var description = IntentDescription("Orders coffee for pickup")
参数摘要必须使用自然语言:
static var parameterSummary: some ParameterSummary {
Summary("Order \(\.$size) \(\.$coffeeType)")
}
// Siri: "Order large latte"
为何重要 元数据不够完善,用户就无法理解意图的作用。元数据越清晰,使用率越高,系统也就越会优先展示。
步骤 6:基于使用情况进行提升
系统会提升用户实际调用的快捷指令和活动。如果没有人使用某个意图,系统就会将其隐藏。
此过程是自动的,你无法控制。你可以控制的是:
- 可发现性 — 让用户更容易找到(步骤 1-5)
- 实用性 — 让它值得使用(设计优质意图)
- 推广 — 向用户展示可用的快捷指令(SiriTipView)
// Promote your shortcuts in-app
SiriTipView(intent: OrderCoffeeIntent(), isVisible: $showTip)
.siriTipViewStyle(.dark)
为何这很重要 即使元数据再完善,如果用户不知道快捷指令的存在,也无济于事。请在应用的 UI 中向用户介绍这些功能。
参见:app-shortcuts-ref,了解 SiriTipView 和 ShortcutsLink 模式
决策树:不同使用场景该选哪种 API
┌─ Need to expose app functionality? ────────────────────────────────┐
│ │
│ ┌─ YES → App Intents (AppIntent protocol) │
│ │ └─ Want instant availability without user setup? │
│ │ └─ YES → App Shortcuts (AppShortcutsProvider) │
│ │ │
│ └─ NO → Exposing app CONTENT (not actions)? │
│ │ │
│ ├─ User-initiated activity (viewing screen)? │
│ │ └─ YES → NSUserActivity with isEligibleForSearch │
│ │ │
│ └─ Indexing all content (documents, orders, notes)? │
│ └─ YES → Core Spotlight (CSSearchableItem) │
│ │
│ ┌─ Already using App Intents? │
│ │ └─ Want automatic Spotlight search for entities? │
│ │ └─ YES → IndexedEntity protocol │
│ │ │
│ └─ Want to connect screen to App Intent entity? │
│ └─ YES → NSUserActivity.appEntityIdentifier │
└──────────────────────────────────────────────────────────────────┘
快速参考表
| 使用场景 | API | 示例 |
|---|---|---|
| 向 Siri/Shortcuts 暴露操作 | AppIntent | “点咖啡” |
| 让操作立即可用 | AppShortcut | 立即显示在 Spotlight 中 |
| 为应用中的所有内容编制索引 | CSSearchableItem | 可搜索所有咖啡订单 |
| 标记当前重要屏幕 | NSUserActivity | 用户正在查看订单详情 |
| 自动生成“查找”操作 | IndexedEntity | “查找满足以下条件的订单……” |
| 将屏幕关联到 App Intent | appEntityIdentifier | 深度链接到特定订单 |
快速实现模式(“一个晚上”方案)
实现最低限度的可发现性:
1. 定义 1-3 个核心 App Intents(30 分钟)
// Your app's most valuable actions
struct OrderCoffeeIntent: AppIntent { /* ... */ }
struct ReorderLastIntent: AppIntent { /* ... */ }
struct ViewOrdersIntent: AppIntent { /* ... */ }
2. 创建 AppShortcutsProvider(15 分钟)
struct CoffeeAppShortcuts: AppShortcutsProvider {
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: OrderCoffeeIntent(),
phrases: ["Order coffee in \(.applicationName)"],
shortTitle: "Order",
systemImageName: "cup.and.saucer.fill"
)
// Add 2-3 more shortcuts
}
}
3. 为顶层内容编制索引(30 分钟)
// Index most recent/important content only
func indexRecentOrders() {
let recentOrders = try await OrderService.shared.recent(limit: 20)
let items = recentOrders.map { createSearchableItem(from: $0) }
CSSearchableIndex.default().indexSearchableItems(items)
}
4. 在详情屏幕中添加 NSUserActivity(30 分钟)
// In your detail view controllers/views
let activity = NSUserActivity(activityType: "com.app.viewOrder")
activity.isEligibleForSearch = true
activity.becomeCurrent()
self.userActivity = activity
5. 在 Spotlight 和 Shortcuts 中测试(15 分钟)
- 打开 Shortcuts 应用 → 搜索你的应用 → 确认快捷指令已显示
- 搜索 Spotlight → 搜索你的内容 → 确认结果已显示
- 调用 Siri → “在 [YourApp] 中点咖啡” → 确认功能正常
总耗时:约 2 小时,即可实现基本的可发现性
大型内容库的批量编制索引
为 1,000 多个项目编制索引时,请分批处理,以避免启动变慢:
func indexAllContent() async {
let allItems = try await ContentService.shared.all()
let batchSize = 100
for batch in stride(from: 0, to: allItems.count, by: batchSize) {
let slice = Array(allItems[batch..<min(batch + batchSize, allItems.count)])
let searchableItems = slice.map { createSearchableItem(from: $0) }
CSSearchableIndex.default().indexSearchableItems(searchableItems) { error in
if let error { print("Batch index error: \(error)") }
}
// Yield between batches to avoid blocking
try? await Task.sleep(for: .milliseconds(50))
}
}
最佳实践:
- 在后台处理中每批编制 100 个项目的索引,不要在启动时执行
- 使用
domainIdentifier对内容分组,以便高效批量删除 - 内容更改时增量重新编制索引(不要为所有内容重新编制索引)
- 对于 50,000 多个项目,使用
CSSearchableIndex.beginBatch()/endBatch()进行原子更新
Spotlight 调试
如果已编制索引的内容没有显示在 Spotlight 中:
验证清单
- 检查索引是否成功 — 为
indexSearchableItems添加完成处理程序日志记录 - 等待处理完成 — Spotlight 可能需要 10-30 秒来处理新项目
- 按完整标题搜索 — Spotlight 初期可能无法匹配部分关键词
- 检查
contentType— 常规内容请使用.item;类型不正确可能会影响排名
常见索引错误
| 问题 | 原因 | 修复方法 |
|---|---|---|
| 内容未显示 | 缺少 title 属性 | 始终设置 attributeSet.title |
| 排名较低 | 没有关键词 | 添加相关的 keywords 数组 |
| 结果过时 | 未删除已移除的项目 | 调用 deleteSearchableItems(withIdentifiers:) |
| 结果重复 | 唯一标识符不稳定 | 使用持久 ID(UUID、数据库主键) |
| 超出配额 | 编制索引的项目过多 | 限制为与用户相关的内容(最近访问、收藏) |
测试 Spotlight 索引
// Verify items are indexed
CSSearchableIndex.default().fetchLastClientState { state, error in
print("Last client state: \(String(describing: state))")
}
// Search programmatically to verify
let query = CSSearchQuery(queryString: "title == 'My Item'*", attributes: ["title"])
query.foundItemsHandler = { items in
print("Found \(items.count) items")
}
query.start()
反模式(请勿这样做)
❌ 反模式 1:只实现 App Intents,不实现 App Shortcuts
问题 用户必须手动配置快捷指令。你的应用不会自动显示在 Spotlight/Siri 中。
修复方法 始终使用建议短语创建 AppShortcutsProvider。
❌ 反模式 2:为 Core Spotlight 中的所有内容编制索引
问题 为数千个项目编制索引会导致性能不佳和配额问题,也会让用户不堪其扰。
// ❌ BAD: Index all 10,000 orders
let allOrders = try await OrderService.shared.all()
修复方法 选择性地编制索引——最近访问的项目、收藏项目和经常访问的项目。
// ✅ GOOD: Index recent orders only
let recentOrders = try await OrderService.shared.recent(limit: 50)
❌ 反模式 3:通用的意图标题和描述
问题 Spotlight 会直接显示这些内容。通用文本会让用户困惑。
// ❌ BAD
static var title: LocalizedStringResource = "Action"
static var description = IntentDescription("Does something")
修正 使用具体、以操作为导向的语言。
// ✅ GOOD
static var title: LocalizedStringResource = "Order Coffee"
static var description = IntentDescription("Orders your favorite coffee for pickup")
❌ 反模式 4:没有向用户介绍快捷指令
问题 即使实现得再完美,如果用户不知道快捷指令的存在,也毫无意义。
修正 使用 SiriTipView 在应用的 UI 中推广快捷指令。
// Show tip after user places order
SiriTipView(intent: ReorderLastIntent(), isVisible: $showTip)
❌ 反模式 5:将每个屏幕都标记为可搜索
问题 系统会难以判断哪些内容重要,导致建议质量低下。
// ❌ BAD: Settings screen marked for prediction
activity.isEligibleForPrediction = true // Don't predict Settings!
修正 只标记用户可能想要返回的屏幕(内容,而非界面装饰)。
// ✅ GOOD: Mark content screens only
if order != nil {
activity.isEligibleForPrediction = true
}
❌ 反模式 6:忘记将 NSUserActivity 与 App Intents 关联
问题 NSUserActivity 和 App Intents 各自独立,错失了集成机会。
修正 使用 appEntityIdentifier 将它们关联起来。
// ✅ GOOD: Connect activity to App Intent entity
activity.appEntityIdentifier = order.id.uuidString
代码审查清单
审查可发现性实现时,请确认:
App Intents:
- 意图具有清晰、以操作为导向的标题
- 描述说明意图的作用
- 参数摘要使用自然语言表述
- 为公开意图设置
isDiscoverable = true
App Shortcuts:
- 已实现 AppShortcutsProvider
- 建议短语包含
\(.applicationName) - 短语简短且以操作为导向
- ShortcutTileColor 与应用品牌相符
- 定义了 3-5 个核心快捷指令(不要太多)
Core Spotlight:
- 仅为有价值的内容建立索引(而非所有内容)
- 唯一标识符稳定且持久
- 域标识符对相关内容进行分组
- 属性包含标题、描述和关键词
- 存在删除逻辑(内容移除时)
NSUserActivity:
- 仅将高价值屏幕标记为可用
- 屏幕出现时调用
becomeCurrent() - 屏幕消失时调用
resignCurrent() -
appEntityIdentifier将 App Intent 实体关联起来 -
contentAttributeSet提供丰富的元数据
用户引导:
- 使用 SiriTipView 推广快捷指令
- 在设置/帮助中提供 ShortcutsLink
- 在引导流程中提及 Siri/Spotlight 支持
测试:
- 快捷指令会显示在“快捷指令”应用中
- Siri 能识别建议短语
- Spotlight 会返回应用内容
- 活动继续功能正常(轻点 Spotlight 结果)
相关技能
- app-intents-ref — 完整的 App Intents API 参考
- app-shortcuts-ref — App Shortcuts 实现指南
- core-spotlight-ref — Core Spotlight 和 NSUserActivity 参考
资源
WWDC:260、275、2022-10170
文档:/appintents/making-your-app-s-functionality-available-to-siri、/corespotlight
技能:axiom-app-intents-ref、axiom-app-shortcuts-ref、axiom-core-spotlight-ref
请记住,可发现性并非单一 API,而是一种策略。通过 App Intents、App Shortcuts、Core Spotlight 和 NSUserActivity 向系统提供元数据。让 iOS 根据上下文和用户行为决定何时展示你的应用。