Highlight
iOS 16 的 App Intents 框架让开发者可以用纯 Swift 代码定义 App Shortcuts,无需额外的 metadata 文件,安装后自动在 Siri、Spotlight 和 Shortcuts 应用中可用。
核心内容
过去,用户需要点击”Add to Siri”按钮或在 Shortcuts 应用中手动设置,才能使用你的 Intent。App Shortcuts 消除了这个门槛——应用安装后,Intent 立即在 Siri、Spotlight 和 Shortcuts 应用中可用。(01:02)
App Intents 是一个全新的 Swift-only 框架,所有定义都在 Swift 源代码中完成,不需要单独的 metadata 文件。(02:57)这带来了几个好处:没有代码生成步骤、不用在源代码编辑器和 metadata 编辑器之间切换、代码审查更方便、合并冲突更容易解决。
以一个冥想应用为例。以前用户要打开应用、登录、找到想听的冥想 session。有了 App Shortcuts,用户直接说”开始冥想”就能启动默认 session。(02:13)
详细内容
基础 App Intent
Intent 是一个实现 AppIntent 协议的 Swift struct,只需要两个东西:标题和 perform 方法。(03:44)
import AppIntents
struct StartMeditationIntent: AppIntent {
static let title: LocalizedStringResource = "Start Meditation Session"
func perform() async throws -> some IntentResult & ProvidesDialog {
await MeditationService.startDefaultSession()
return .result(dialog: "Okay, starting a meditation session.")
}
}
关键点:
title用于在 Shortcuts 应用中显示这个 Intentperform()是 async 的,可以执行异步操作- 返回
.result(dialog:)向用户展示反馈 - 如果应用做了本地化,dialog 字符串也需要本地化
创建 App Shortcut
有了 Intent 还不够,需要把它包装成 App Shortcut。实现 AppShortcutsProvider 协议:(05:31)
import AppIntents
struct MeditationShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: StartMeditationIntent(),
phrases: [
"Start a \(.applicationName)",
"Begin \(.applicationName)",
"Meditate with \(.applicationName)",
"Start a session with \(.applicationName)"
]
)
}
}
关键点:
- 用
\(.applicationName)代替硬编码应用名,Siri 会自动插入主应用名和已配置的同义词 - 提供多个短语变体,覆盖用户可能的不同说法
- 每个应用最多 10 个 App Shortcut,但大多数应用只需要几个
- 短语需要本地化到应用支持的所有语言
自定义 Snippet 视图
默认情况下,Siri 运行 Intent 时显示一个通用视图。你可以返回自定义的 SwiftUI 视图,让它更有信息量。(07:40)
func perform() async throws -> some ProvidesDialog & ShowsSnippetView {
await MeditationService.startDefaultSession()
return .result(
dialog: "Okay, starting a meditation session.",
view: MeditationSnippetView()
)
}
关键点:
- 自定义视图使用 SwiftUI,和 Widget 使用同样的视图技术
- 不需要单独创建 UI extension
- 视图不能包含交互或动画(和 Widget 一样的限制)
- 可以在三个阶段展示自定义 UI:值确认、Intent 确认、Intent 完成后
添加参数
让用户指定具体的冥想 session 类型,需要添加参数。首先定义参数类型,实现 AppEntity 协议:(10:09)
import AppIntents
struct MeditationSession: AppEntity {
let id: UUID
let name: LocalizedStringResource
static var typeDisplayName: LocalizedStringResource = "Meditation Session"
var displayRepresentation: AppIntents.DisplayRepresentation {
DisplayRepresentation(title: name)
}
static var defaultQuery = MeditationSessionQuery()
}
然后实现查询协议,让 App Intents 框架能根据标识符查找实体:(10:55)
struct MeditationSessionQuery: EntityQuery {
func entities(for identifiers: [UUID]) async throws -> [MeditationSession] {
return identifiers.compactMap { SessionManager.session(for: $0) }
}
}
在 Intent 中添加参数:(11:16)
struct StartMeditationIntent: AppIntent {
@Parameter(title: "Session Type")
var sessionType: SessionType?
// ...
}
提示用户选择值
如果用户没有提供参数值,可以主动提示。App Intents 支持三种提示类型:歧义消除(从固定列表选)、值提示(开放式值)、确认(验证特定值)。(12:14)
func perform() async throws -> some ProvidesDialog {
let sessionToRun = self.session ?? try await $session.requestDisambiguation(
among: SessionManager.allSessions,
dialog: IntentDialog("What session would you like?")
)
await MeditationService.start(session: sessionToRun)
return .result(
dialog: "Okay, starting a \(sessionToRun.name) meditation session."
)
}
关键点:
- 使用
$session.requestDisambiguation从固定列表中让用户选择 - App Intents 会用每个 session 的
displayRepresentation格式化列表项 - 用户选择后,选中的项会返回给
perform方法 - 提示会拖慢交互,不要过度使用
参数化短语
让用户在初始指令中直接指定参数值,比如”开始一个平静冥想”。这需要三个步骤:(15:27)
第一步,在 Query 中实现 suggestedEntities():
struct MeditationSessionQuery: EntityQuery {
func entities(for identifiers: [UUID]) async throws -> [MeditationSession] {
return identifiers.compactMap { SessionManager.session(for: $0) }
}
func suggestedEntities() async throws -> [MeditationSession] {
return SessionManager.allSessions
}
}
关键点:这个方法必须实现,否则不会生成参数化短语。
第二步,在模型层通知 App Intents 参数值变化:(16:34)
class SessionModel {
@Published
var sessions: [MeditationSession] = []
private var cancellable: AnyCancellable?
init() {
self.cancellable = $sessions.sink { _ in
MeditationShortcuts.updateAppShortcutParameters()
}
}
}
关键点:
updateAppShortcutParameters()是 App Intents 框架提供的方法- 调用后,框架会重新查询实体列表,更新参数化短语
- 通常在从服务器获取新 session 后调用
第三步,在 App Shortcut 中添加带参数的短语:(17:09)
struct MeditationShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: StartMeditationIntent(),
phrases: [
"Start a \(.applicationName)",
"Begin \(.applicationName)",
"Meditate with \(.applicationName)",
"Start a \($session) session with \(.applicationName)",
"Begin a \($session) session with \(.applicationName)",
"Meditate on \($session) with \(.applicationName)"
]
)
}
}
关键点:
- 用
\($session)引用 Intent 中的参数 - 框架会把参数值列表与短语组合,生成所有变体
- 参数值的显示文本来自
DisplayRepresentation的title
可发现性
App Shortcuts 安装即可用,但用户需要知道它们存在。(17:55)
- Siri Tip:在应用内合适时机展示,SwiftUI 和 UIKit 都支持,有多种样式
- ShortcutsLink:跳转到 Shortcuts 应用中该应用的所有 shortcut 列表
- Siri 主动推荐:用户问”What can I do here?”时自动推荐
注意事项
App Shortcuts 在应用首次启动前就可能被运行。(20:36)如果应用需要登录,Intent 应该优雅地失败,提示用户需要先登录。参数化短语在应用首次启动并通知框架前不可用,所以建议至少保留几个非参数化短语。
核心启发
1. 为高频操作添加零配置的语音入口
找出应用中用户每天会做的操作,用 App Shortcuts 提供一句话触达。比如健身应用的”开始跑步”、记账应用的”记录一笔支出”。实现成本很低——一个 Intent struct 和一个 AppShortcutsProvider 就能让功能在 Siri 和 Spotlight 中可用。
2. 用参数化短语减少交互步骤
如果你的应用有固定类别的内容(比如播放列表、收藏餐厅、常用房间),把类别名做成参数,让用户说”播放我的跑步歌单”而不是”播放音乐”然后被追问”播放什么”。实现 suggestedEntities() 并在数据变化时调用 updateAppShortcutParameters() 即可。
3. 在应用内 strategically 展示 Siri Tip
不要随机弹窗。选择用户完成某个操作后可能想下次快速重复的时刻,比如刚手动下单后展示”下次可以说’查看我的订单‘“的 tip。SwiftUI 中直接用 SiriTipView,UIKit 用对应的视图。支持关闭回调,记录用户偏好避免重复打扰。
4. 为未登录状态设计优雅降级
App Shortcuts 可能在用户从未打开过应用时就被触发。在 perform() 中检查必要状态(如登录),如果条件不满足,返回一个友好的 dialog 引导用户打开应用完成设置,而不是崩溃或静默失败。
关联 Session
- Design App Shortcuts — App Shortcuts 的设计最佳实践,包含命名、视觉呈现和信息收集策略
- Dive into App Intents — 深入了解 App Intents 框架的高级用法
- Add Live Activities to Your App — 在 App Shortcuts 中使用 Live Activities 展示实时信息
评论
GitHub Issues · utterances