探索 App Intents 的增强功能
Explore enhancements to App Intents
2023年6月5日
一句话判断
App Intents 今年全面接管 Widget 配置(替代 SiriKit Intent Definition File)、实现 Widget 交互(按钮和开关触发代码)、新增 IntentParameterDependency 实现参数联动,还扩展了 Shortcuts 集成和静态提取能力。
这场 Session 讲了什么
Shortcuts 团队的 Roman Efimov 介绍了 App Intents 在 iOS 17 中的全面增强,涵盖三个方向:Widget 集成、开发者体验改进和 Shortcuts 集成更新。
Widget 配置用 App Intents:过去配置 Widget 需要 Xcode 中的 Intent Definition File。现在可以直接在 Widget Extension 代码中定义 WidgetConfigurationIntent,使用 AppIntentConfiguration 替代 IntentConfiguration。Xcode 提供一键迁移按钮,将 SiriKit Intent 自动转换为 App Intent 代码。
Widget 交互:SwiftUI 的 Button 和 Toggle 现在支持 App Intents。在 Widget 视图中将 App Intent 关联到按钮,用户点击就能直接执行应用代码(如设置闹钟、切换设置),不需要打开应用。
IntentParameterDependency:新增的属性包装器,允许在 DynamicOptionsProvider 或 Query 中访问其他参数的值。比如根据用户选择的公交站点过滤可用路线。这个 API 在 Widget、Shortcuts 和 Focus Filters 中通用。
开发者体验改进:框架支持扩展(更多框架的实体和枚举可用作参数)、静态提取增强(编译时发现更多 App Intent 信息)。
Shortcuts 集成:App Intent 代码可以同时服务于 Widget 配置、Widget 交互和 Shortcuts 动作——一个 Intent 定义,三种用途。
值得深挖的点
AppIntentConfiguration 的统一力量:一个 WidgetConfigurationIntent 既是 Widget 的配置描述,也是 Shortcuts 中的可发现动作。SetAlarm Intent 既是 Widget 中按钮触发的动作,也是 Shortcuts 中可搜索的操作。这种复用大幅减少了代码量。
IntentParameterDependency 的级联过滤:这是参数联动的核心机制。用户选择站点后,路线选项自动过滤为该站点的可用路线。实现方式是在 Query 中用 @IntentParameterDependency 引用 Intent 的参数,然后在 results() 方法中读取这些参数来动态过滤。
SiriKit 到 App Intents 的一键迁移:Xcode 可以自动将 Intent Definition File 转换为 App Intent 代码。迁移后需要确保参数名和类型完全匹配,否则已有 Widget 会在更新后失效。用户更新应用时 Widget 会自动迁移,但只有一次机会。
静态提取的增强:编译器现在能从更多代码位置提取 App Intent 的元数据(标题、描述、参数),让 Siri 和 Shortcuts 能更好地发现和理解你的 App Intent。
代码片段
// Widget 配置 - 使用 AppIntentConfiguration
struct NextBusWidget: Widget {
var body: some WidgetConfiguration {
AppIntentConfiguration(
kind: "NextBus",
intent: NextBusConfigurationIntent.self,
provider: NextBusProvider()
) { entry in
NextBusWidgetEntryView(entry: entry)
}
}
}
// 配置 Intent - 直接在代码中定义
struct NextBusConfigurationIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Next Bus"
static var description = IntentDescription("Show next bus arrival")
@Parameter(title: "Bus Stop")
var busStop: BusStopEntity
@Parameter(title: "Route")
var route: RouteEntity
@Parameter(title: "Direction")
var direction: Direction
}
// Widget 交互 - 按钮触发 App Intent
struct SetAlarmIntent: AppIntent {
static var title: LocalizedStringResource = "Set Alarm"
@Parameter(title: "Departure Time")
var departureTime: Date
func perform() async throws -> some IntentResult {
// 在应用中设置闹钟
AlarmManager.setAlarm(at: departureTime)
return .result()
}
}
// Widget 视图中使用
Button(intent: SetAlarmIntent(departureTime: entry.nextBusTime)) {
Text(entry.nextBusTime.formatted())
}
// IntentParameterDependency - 参数联动
struct RouteQuery: EntityQuery {
@IntentParameterDependency(\.busStop)
var busStop: BusStopEntity?
func results() async throws -> [RouteEntity] {
guard let stop = busStop else { return [] }
// 根据选择的站点过滤路线
return RouteManager.routes(for: stop)
}
}
// 动态选项提供者
struct BusStopOptionsProvider: DynamicOptionsProvider {
func results() async throws -> [BusStopEntity] {
// 提供所有可用的公交站点
return BusStopManager.allStops.map { stop in
BusStopEntity(id: stop.id, name: stop.name)
}
}
}
最佳实践
- 新项目直接用 AppIntentConfiguration:不再需要 Intent Definition File,所有配置在代码中完成。
- 一个 Intent 多种用途:Widget 配置、Widget 交互和 Shortcuts 动作可以共享同一个 App Intent 实现。
- 迁移时保持 Schema 一致:从 SiriKit 迁移时,参数名和类型必须完全匹配。添加新参数时用可选或带默认值的方式。
- 利用 IntentParameterDependency 做级联选择:当参数之间有依赖关系时(如站点->路线),用这个 API 提供上下文感知的选项。
- 测试迁移只做一次:用户更新应用后 Widget 自动迁移,只有一次机会。发布前要充分测试。
还有什么值得关注
- App Intents 正在成为 Apple 平台集成的统一入口——Widget、Siri、Shortcuts、Focus Filters 都通过它工作。
- 静态提取的增强意味着你不需要做额外注册工作,编译器自动发现你的 Intent。
- Session 推荐配合 “Dive into App Intents” 和 “Bring your widget to life” 一起看。
- 动态选项和查询(DynamicOptionsProvider / EntityQuery)是 App Intents 体系中最强大的部分,值得深入学习。