关于 relevancekit
relevancekit 是面向 Apple Watch 小组件开发者的指南,用相关性线索影响 Smart Stack 中小组件的显示时机。它介绍时间、位置、健身、睡眠和硬件状态等线索及其权限要求。
主要功能
- 01按时间、位置和活动设置相关性
- 02构建由相关性驱动的小组件
- 03组合并排序多个相关性线索
- 04配置小组件分组与关联类型
- 05检查位置、健身和睡眠权限
适用场景
- 01在活动开始前显示日程小组件
- 02在通勤或特定地点提示相关信息
- 03根据锻炼、睡眠或耳机状态显示卡片
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill relevancekit安装后在 Claude Code 中输入/relevancekit确认已加载。
附带文件evals/evals.jsonreferences/relevancekit-patterns.md
详细介绍
RelevanceKit
提供设备端上下文线索,提升小组件在 Apple Watch 智能叠放中的显示优先级。RelevanceKit 根据时间、位置、健身状态、睡眠计划或连接的硬件,告知系统小组件何时相关。面向 Swift 6.3 / watchOS 26+。
Beta 敏感内容。 在对 RelevanceKit 的可用性或行为作出确定性判断前,请重新查阅 Apple 文档。
完整的相关小组件、时间线提供程序、分组、预览和权限模式,请参阅 references/relevancekit-patterns.md。
目录
概览
watchOS 使用两种机制来确定小组件在智能叠放中的相关性:
- 时间线提供程序相关性 -- 在现有
AppIntentTimelineProvider上实现relevance(),为时间线条目附加RelevantContext线索。此功能可用于多个平台;只有 watchOS 会根据这些数据采取行动。 - 相关小组件 -- 使用
RelevanceConfiguration和RelevanceEntriesProvider构建完全由相关性线索驱动的小组件。仅适用于 watchOS 26+。系统会为每个相关条目创建单独的智能叠放卡片。
如果小组件始终有数据要显示,且相关性只是辅助信息,请选择时间线提供程序。如果小组件只应在条件匹配时出现,或需要同时显示多张卡片(例如多个即将开始的日历事件),请选用相关小组件。
关键类型
| 类型 | 模块 | 作用 |
|---|---|---|
RelevantContext | RelevanceKit | 上下文线索(日期、位置、健身、睡眠、硬件) |
WidgetRelevance | WidgetKit | 某种小组件类型的相关性属性集合 |
WidgetRelevanceAttribute | WidgetKit | 将小组件配置与 RelevantContext 配对 |
WidgetRelevanceGroup | WidgetKit | 控制智能叠放中的分组行为 |
RelevanceConfiguration | WidgetKit | 由相关性线索驱动的小组件配置(watchOS 26+) |
RelevanceEntriesProvider | WidgetKit | 为采用相关性配置的小组件提供条目(watchOS 26+) |
RelevanceEntry | WidgetKit | 渲染一张相关小组件卡片所需的数据(watchOS 26+) |
RelevanceConfiguration、RelevanceEntriesProvider 和 RelevanceEntry 都是 WidgetKit API。只有当它们属于 watchOS 相关小组件工作流、用于提供 RelevanceKit 线索时,才将其纳入本技能的范围。
设置
导入
import RelevanceKit
import WidgetKit
平台可用性
RelevantContext 在多个平台上均有声明(iOS 17+、watchOS 10+),但 RelevanceKit 功能只有在 watchOS 上才会生效。在其他平台调用该 API 不会产生任何效果。时间线提供程序的 relevance() 适用于 iOS 18+、macOS 15+、visionOS 26+ 和 watchOS 11+,可用于共享的提供程序代码。RelevanceConfiguration、RelevanceEntriesProvider 和 RelevanceEntry 仅适用于 watchOS 26+。
权限
某些相关性线索需要授权或目标配置:
| 线索 | 所需权限 |
|---|---|
.location(inferred:) | 宿主应用请求位置访问权限;Widget 扩展声明 NSWidgetWantsLocation |
.location(_:) (CLRegion) | 宿主应用请求位置访问权限;Widget 扩展声明 NSWidgetWantsLocation |
.location(category:) | 宿主应用请求位置访问权限;Widget 扩展声明 NSWidgetWantsLocation |
.fitness(.workoutActive) | HealthKit 对 HKWorkoutType 的访问权限 |
.fitness(.activityRingsIncomplete) | HealthKit 对 appleExerciseTime、appleMoveTime 和 appleStandTime 的访问权限 |
.sleep(_:) | HealthKit sleepAnalysis 权限 |
.hardware(headphones:) | 无 |
.date(...) | 无 |
将位置用途说明添加到宿主应用的 Info.plist 中,而不只是 Widget 扩展。
在 Widget 代码中,依赖位置线索之前,请检查
CLLocationManager.isAuthorizedForWidgetUpdates。对于健身和睡眠线索,请启用 HealthKit,
并在提供相关性的应用和 Widget 扩展目标中请求确切的读取类型。
相关性提供程序
选项 1:带相关性的时间线提供程序
向现有的 AppIntentTimelineProvider 添加 relevance() 方法。此
方法可在 iOS 和 watchOS 之间共享代码,同时为 watchOS Smart Stack 添加智能功能。
struct MyProvider: AppIntentTimelineProvider {
// ... snapshot, timeline, placeholder ...
func relevance() async -> WidgetRelevance<MyWidgetIntent> {
let attributes = events.map { event in
let context = RelevantContext.date(
from: event.startDate,
to: event.endDate
)
return WidgetRelevanceAttribute(
configuration: MyWidgetIntent(event: event),
context: context
)
}
return WidgetRelevance(attributes)
}
}
选项 2:RelevanceEntriesProvider(watchOS 26+)
构建一个仅在条件满足时才显示的 Widget。系统会调用
relevance() 来了解 Widget 何时重要,然后调用 entry() 并传入
匹配的配置以获取渲染数据。
@available(watchOS 26.0, *)
struct MyRelevanceProvider: RelevanceEntriesProvider {
func relevance() async -> WidgetRelevance<MyWidgetIntent> {
let attributes = events.map { event in
WidgetRelevanceAttribute(
configuration: MyWidgetIntent(event: event),
context: RelevantContext.date(event.date, kind: .scheduled)
)
}
return WidgetRelevance(attributes)
}
func entry(
configuration: MyWidgetIntent,
context: Context
) async throws -> MyRelevanceEntry {
if context.isPreview {
return .preview
}
return MyRelevanceEntry(event: configuration.event)
}
func placeholder(context: Context) -> MyRelevanceEntry {
.placeholder
}
}
边界路由
当某项功能同时涉及 Widget、位置、锻炼和 Smart Stack 相关性时,
让 RelevanceKit 专注于 RelevantContext、WidgetRelevanceAttribute、提供程序的
relevance()、RelevantIntentManager、相关 Widget 的交接,以及
相关性线索所需的权限。将时间线、重新加载预算、类型、渲染、
APNs Widget 推送、实时活动和 Widget 控件交由
WidgetKit 处理;将 HKWorkoutSession、HKLiveWorkoutBuilder、HKWorkoutRoute、
查询、活动圆环/睡眠数据和授权 UX 交由 HealthKit 处理;将 MKLocalSearch、
MKLocalSearchCompleter、MKDirections、地理编码、
授权、区域、地理围栏和地点数据交由 MapKit/CoreLocation 处理。
基于时间的相关性
时间线索会告知系统 Widget 在特定时刻或该时刻附近很重要。
单个日期
RelevantContext.date(eventDate)
带类型的日期
DateKind 会提供有关时间相关性性质的额外提示:
| 类型 | 用途 |
|---|---|
.default | 通用时间相关性 |
.scheduled | 预定事件(会议、航班) |
.informational | 某个时段附近相关的信息(天气预报) |
RelevantContext.date(meetingStart, kind: .scheduled)
日期范围
// Using from/to
RelevantContext.date(from: startDate, to: endDate)
// Using DateInterval
RelevantContext.date(interval: dateInterval, kind: .scheduled)
// Using ClosedRange
RelevantContext.date(range: startDate...endDate, kind: .default)
基于位置的相关性
推断位置
系统会根据个人日常活动推断某些位置。无需坐标。
RelevantContext.location(inferred: .home)
RelevantContext.location(inferred: .work)
RelevantContext.location(inferred: .school)
RelevantContext.location(inferred: .commute)
应用权限表中的位置条目,并在返回线索之前检查
CLLocationManager.isAuthorizedForWidgetUpdates。
特定区域
import CoreLocation
let region = CLCircularRegion(
center: CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090),
radius: 500,
identifier: "apple-park"
)
RelevantContext.location(region)
兴趣点类别(26.0+ SDK)
指示在给定类别的任意地点附近时的相关性。如果类别不受支持,则返回 nil。该工厂在 Apple 平台 26.0+ 上可通过 SDK 使用,
但 RelevanceKit 线索仍仅影响 watchOS 上的 Smart Stack 行为。
import MapKit
if let context = RelevantContext.location(category: .beach) {
// Widget is relevant whenever the person is near a beach
}
健身与睡眠相关性
健身
// Relevant when activity rings are incomplete
RelevantContext.fitness(.activityRingsIncomplete)
// Relevant during an active workout
RelevantContext.fitness(.workoutActive)
应用权限中的确切健身映射。
睡眠
// Relevant around bedtime
RelevantContext.sleep(.bedtime)
// Relevant around wakeup
RelevantContext.sleep(.wakeup)
应用权限中的睡眠映射。
硬件相关性
// Relevant when headphones are connected
RelevantContext.hardware(headphones: .connected)
无需特殊权限。
组合信号
在 WidgetRelevance 数组中返回多个 WidgetRelevanceAttribute 值,使 Widget
在多种不同条件下都具有关联性。
func relevance() async -> WidgetRelevance<MyIntent> {
var attributes: [WidgetRelevanceAttribute<MyIntent>] = []
// Relevant during morning commute
attributes.append(
WidgetRelevanceAttribute(
configuration: MyIntent(mode: .commute),
context: .location(inferred: .commute)
)
)
// Relevant at work
attributes.append(
WidgetRelevanceAttribute(
configuration: MyIntent(mode: .work),
context: .location(inferred: .work)
)
)
// Relevant around a scheduled event
for event in upcomingEvents {
attributes.append(
WidgetRelevanceAttribute(
configuration: MyIntent(eventID: event.id),
context: .date(event.date, kind: .scheduled)
)
)
}
return WidgetRelevance(attributes)
}
顺序很重要。 按优先级顺序返回相关性属性。系统可能只使用所提供相关性的一部分。
小组件集成
使用 RelevanceConfiguration 的相关小组件
@available(watchOS 26, *)
struct MyRelevantWidget: Widget {
var body: some WidgetConfiguration {
RelevanceConfiguration(
kind: "com.example.relevant-events",
provider: MyRelevanceProvider()
) { entry in
EventWidgetView(entry: entry)
}
.configurationDisplayName("Events")
.description("Shows upcoming events when relevant")
}
}
与时间线小组件关联
当时间线小组件和相关小组件显示相同数据时,使用
associatedKind 避免重复卡片。系统会在相关小组件得到推荐时,用相关小组件卡片替换时间线
小组件卡片。
RelevanceConfiguration(
kind: "com.example.relevant-events",
provider: MyRelevanceProvider()
) { entry in
EventWidgetView(entry: entry)
}
.associatedKind("com.example.timeline-events")
分组
WidgetRelevanceGroup 控制系统如何在 Smart Stack 中对小组件进行分组。
// Opt out of default per-app grouping so each card appears independently
WidgetRelevanceAttribute(
configuration: intent,
group: .ungrouped
)
// Named group -- only one widget from the group appears at a time
WidgetRelevanceAttribute(
configuration: intent,
group: .named("weather-alerts")
)
// Default system grouping
WidgetRelevanceAttribute(
configuration: intent,
group: .automatic
)
RelevantIntent(时间线提供器路径)
使用时间线提供器时,还要更新 RelevantIntentManager,以便系统在时间线刷新间隔内保留
相关性数据。
import AppIntents
func updateRelevantIntents() async {
let intents = events.map { event in
RelevantIntent(
MyWidgetIntent(event: event),
widgetKind: "com.example.events",
relevance: RelevantContext.date(from: event.start, to: event.end)
)
}
try? await RelevantIntentManager.shared.updateRelevantIntents(intents)
}
每当相关性数据发生变化时都调用此方法——不应只在时间线刷新期间调用。
预览相关小组件
使用预览配方中的条目、相关性配置和完整提供器配方。在 Apple Watch 上启用 WidgetKit 开发者模式,测试已授予和已拒绝权限的情况,并在实体 Apple Watch 上完成测试;请参阅 测试技巧。
常见错误
- 使用 RelevanceKit API 并期待 iOS 行为。 该 API 可在所有平台上编译,但只有在 watchOS 上才会生效。
- Smart Stack 卡片重复。 同时为相同数据提供时间线小组件和相关小组件时,使用
.associatedKind(_:)避免重复。 - 未调用
updateRelevantIntents。 使用时间线提供器时,如果只在timeline()中调用此方法,系统在刷新间隔内的相关性数据就会过期。每当数据发生变化时都要更新。 - 忽略
location(category:)返回的 nil。 此工厂方法返回可选值。并非所有MKPointOfInterestCategory值都受支持。
审查清单
- 路由:RelevanceKit 仅在 watchOS 上生效,WidgetKit、 HealthKit、MapKit 和 CoreLocation 实现仍处于同级范围内。
- 信号:上下文与数据模型匹配,属性按优先级排序, 且每条线索都使用 Permissions 表中的准确设置。
- 提供器:条目、占位内容、相关性和预览路径均已提供; 已处理位置类别的可选值和小组件更新授权。
- 协调:
.associatedKind(_:)可避免卡片重复,且 每当时间线提供器数据发生变化时都会调用updateRelevantIntents。 - 测试:已启用开发者模式,且预览涵盖不同显示尺寸以及 已授予和已拒绝权限的状态。