Build widgets for the Smart Stack on Apple Watch
Swift & UI 进阶 20m

为 Apple Watch 智能叠放构建 Widget

Build widgets for the Smart Stack on Apple Watch

2023年6月5日

在 Apple 官方观看视频

一句话判断

watchOS 10 的 Smart Stack 需要你的 Widget 告诉系统”什么时候我最相关”——通过 TimelineEntryRelevance 的 score 和 duration,以及 RelevantIntentManager 的日期标记,让系统在鸟类来访时自动展示你的 Widget。

这场 Session 讲了什么

watchOS 团队的 Calvin Gaisford 以 Backyard Birds 应用为例,完整演示了如何为 Apple Watch 的 Smart Stack 构建 Widget。

Smart Stack 是 watchOS 10 中展示 Widget 的新方式。系统根据相关性自动选择显示哪个 Widget。开发者需要做两件事来帮助系统做出正确的选择:在 TimelineEntry 中标记相关性(score 和 duration),通过 RelevantIntentManager 注册相关日期。

Widget 使用 AppIntentConfiguration(而非旧的 IntentConfiguration)。配置 Intent 遵循 WidgetConfigurationIntent 协议,直接在 Widget Extension 代码中定义,不需要 Intent Definition File。

Session 完整覆盖了 Widget 的构建流程:定义 ConfigurationIntent、创建 TimelineEntry(含 relevance)、实现 TimelineProvider(placeholder/snapshot/timeline/recommendations)、构建视图、设置相关性管理。

Xcode 新增了 Widget 时间线预览功能,可以在 Canvas 中直接查看 Widget 的不同时间状态。

值得深挖的点

TimelineEntryRelevance 的双参数设计:score 用于同一 Widget 的时间线内部排序(如鸟类来访时 score=10,没有鸟类时 score=0),duration 告诉系统这个相关性持续多久。系统综合所有 Widget 的 relevance 来决定 Smart Stack 中展示什么。

RelevantIntentManager 的外部触发:除了时间线内的 relevance,你还可以通过 RelevantIntentManager 向系统注册特定日期的 intent。比如”后天下午 3 点有个观鸟活动”,系统会在那个时间优先展示你的 Widget。

recommendations 函数的预配置 Widget:这个函数返回一组预配置的 Widget 建议,出现在 Widget 画廊中。对于 Backyard Birds,每个后院就是一个预配置 Widget。

Xcode Widget 时间线预览:Canvas 底部展示时间线条目序列,可以直观看到 Widget 在不同时间点的显示效果。这比运行应用来测试 Widget 高效得多。

代码片段

// Widget 配置 - 使用 AppIntentConfiguration
@main
struct BackyardVisitorsWidget: Widget {
    var body: some WidgetConfiguration {
        AppIntentConfiguration(
            kind: "BackyardVisitors",
            intent: BackyardWidgetIntent.self,
            provider: BackyardProvider()
        ) { entry in
            BackyardBirdsWidgetEntryView(entry: entry)
        }
    }
}
// TimelineEntry 添加 relevance
struct SimpleEntry: TimelineEntry {
    let date: Date
    let configuration: BackyardWidgetIntent
    let backyard: Backyard

    // 标记此条目的相关性
    var relevance: TimelineEntryRelevance? {
        if let visitor = backyard.visitorEvent(at: date) {
            // 有鸟类来访:高相关性
            return TimelineEntryRelevance(
                score: 10.0,
                duration: visitor.endDate.timeIntervalSince(date)
            )
        }
        // 没有鸟类:低相关性
        return TimelineEntryRelevance(score: 0.0)
    }
}
// TimelineProvider - recommendations 提供预配置 Widget
func recommendations() -> [AppIntentConfiguration<BackyardWidgetIntent, SimpleEntry>.Recommendation] {
    // 为每个后院生成一个推荐的 Widget 配置
    let backyards = Backyard.allBackyards
    return backyards.map { backyard in
        AppIntentConfiguration.Recommendation(
            intent: BackyardWidgetIntent(backyardID: backyard.id),
            description:LocalizedStringResource(stringLiteral: backyard.name)
        )
    }
}
// RelevantIntentManager - 注册特定日期的相关性
let manager = RelevantIntentManager()
let intent = BackyardWidgetIntent(backyardID: "yard-123")
let relevantDate = Calendar.current.date(byAdding: .day, value: 2, to: Date())!

let relevantIntent = RelevantIntent(
    intent: intent,
    validDays: [relevantDate],
    validTimes: [RelevantTime(beginHour: 14, beginMinute: 0,
                              endHour: 17, endMinute: 0)]
)
manager.relevantIntents = [relevantIntent]

最佳实践

  • relevance score 要有意义地分层:鸟类来访时给高分,没有时给零分。系统会比较所有 Widget 的 score 来决定展示优先级。
  • 为每个逻辑单元提供预配置 Widget:通过 recommendations() 让用户快速选择感兴趣的 Widget 变体。
  • 利用 RelevantIntentManager 注册未来事件:如果应用知道某些时间点特别重要(如预约、活动),提前注册让系统在合适时机展示。
  • 用 Xcode Canvas 预览时间线:不要每次都运行应用来测试 Widget,Canvas 的时间线预览足够高效。
  • placeholder 和 snapshot 要快速返回:这两个函数需要即时响应,使用本地数据或随机数据即可。

还有什么值得关注

  • Smart Stack 是 watchOS 10 的核心交互方式之一,Widget 的可见性完全取决于相关性评分。
  • AppIntentConfiguration 是新标准,SiriKit Intent Configuration 已经过时。新项目应直接使用 App Intents。
  • Widget 的时间线粒度需要平衡——太多条目消耗资源,太少条目无法精确表达相关性变化。
WWDC 2023