relevancekit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRelevanceKit
RelevanceKit
Provide on-device contextual clues that increase a widget's visibility in the
Smart Stack on Apple Watch. RelevanceKit tells the system when a widget is
relevant -- by time, location, fitness state, sleep schedule, or connected
hardware -- so the Smart Stack can surface the right widget at the right moment.
Targets Swift 6.3 / watchOS 26+.
Beta-sensitive. RelevanceKit shipped with watchOS 26. Re-check Apple documentation before making strong claims about API availability or behavior.
See references/relevancekit-patterns.md for complete code patterns including
relevant widgets, timeline provider integration, grouping, previews, and
permission handling.
提供设备端上下文线索,提升Apple Watch上Smart Stack中Widget的可见性。RelevanceKit会告知系统Widget的相关时机——比如基于时间、位置、健身状态、睡眠计划或已连接硬件——让Smart Stack能在合适的时刻展示对应的Widget。目标适配Swift 6.3 / watchOS 26+。
Beta版本注意事项:RelevanceKit随watchOS 26发布。在对API的可用性或行为做出明确结论前,请重新查阅Apple官方文档。
完整的代码模式(包括相关Widget、时间线提供器集成、分组、预览和权限处理)请查看references/relevancekit-patterns.md。
Contents
目录
Overview
概述
watchOS uses two mechanisms to determine widget relevance in the Smart Stack:
- Timeline provider relevance -- implement on an existing
relevance()to attachAppIntentTimelineProviderclues to timeline entries. Available across platforms; only watchOS acts on the data.RelevantContext - Relevant widget -- use with a
RelevanceConfigurationto build a widget driven entirely by relevance clues. The system creates individual Smart Stack cards per relevant entry. watchOS 26+ only.RelevanceEntriesProvider
Choose a timeline provider when the widget always has data to show and relevance
is supplementary. Choose a relevant widget when the widget should only appear
when conditions match, or when multiple cards should appear simultaneously (e.g.,
several upcoming calendar events).
watchOS通过两种机制确定Smart Stack中Widget的相关性:
- 时间线提供器相关性——在现有的上实现
AppIntentTimelineProvider方法,将relevance()线索附加到时间线条目上。该API支持多平台,但仅watchOS会实际使用这些数据。RelevantContext - 相关性Widget——结合与
RelevanceConfiguration构建完全由相关性线索驱动的Widget。系统会为每个相关条目创建独立的Smart Stack卡片。仅适用于watchOS 26+。RelevanceEntriesProvider
如果Widget始终有数据展示,且相关性仅作为补充功能,选择时间线提供器方案。如果Widget仅需在满足特定条件时显示,或需要同时展示多张卡片(例如多个即将到来的日历事件),则选择相关性Widget方案。
Key Types
核心类型
| Type | Module | Role |
|---|---|---|
| RelevanceKit | A contextual clue (date, location, fitness, sleep, hardware) |
| WidgetKit | Collection of relevance attributes for a widget kind |
| WidgetKit | Pairs a widget configuration with a |
| WidgetKit | Controls grouping behavior in the Smart Stack |
| WidgetKit | Widget configuration driven by relevance clues (watchOS 26+) |
| WidgetKit | Provides entries for a relevance-configured widget (watchOS 26+) |
| WidgetKit | Data needed to render one relevant widget card (watchOS 26+) |
| 类型 | 模块 | 作用 |
|---|---|---|
| RelevanceKit | 上下文线索(日期、位置、健身、睡眠、硬件) |
| WidgetKit | 某类Widget的相关性属性集合 |
| WidgetKit | 将Widget配置与 |
| WidgetKit | 控制Smart Stack中的分组行为 |
| WidgetKit | 由相关性线索驱动的Widget配置(仅watchOS 26+) |
| WidgetKit | 为相关性配置的Widget提供条目数据(仅watchOS 26+) |
| WidgetKit | 渲染单个相关性Widget卡片所需的数据(仅watchOS 26+) |
Setup
设置
Import
导入模块
swift
import RelevanceKit
import WidgetKitswift
import RelevanceKit
import WidgetKitPlatform Availability
平台可用性
RelevantContextRelevanceConfigurationRelevanceEntriesProviderRelevanceEntryRelevantContextRelevanceConfigurationRelevanceEntriesProviderRelevanceEntryPermissions
权限配置
Certain relevance clues require the user to grant permission to both the app
and the widget extension:
| Clue | Required Permission |
|---|---|
| Location access |
| Location access |
| Location access |
| HealthKit workout/activity rings permission |
| HealthKit |
| None |
| None |
Request permissions in both the main app target and the widget extension target.
某些相关性线索需要用户同时向主应用和Widget扩展授予权限:
| 线索类型 | 所需权限 |
|---|---|
| 位置访问权限 |
| 位置访问权限 |
| 位置访问权限 |
| HealthKit workout/activity rings权限 |
| HealthKit |
| 无 |
| 无 |
请在主应用目标和Widget扩展目标中均请求相应权限。
Relevance Providers
相关性提供器
Option 1: Timeline Provider with Relevance
方案1:带相关性的时间线提供器
Add a method to an existing . This
approach shares code across iOS and watchOS while adding watchOS Smart Stack
intelligence.
relevance()AppIntentTimelineProviderswift
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)
}
}在现有的中添加方法。该方案可在iOS和watchOS间共享代码,同时为watchOS的Smart Stack增加智能展示能力。
AppIntentTimelineProviderrelevance()swift
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)
}
}Option 2: RelevanceEntriesProvider (watchOS 26+)
方案2:RelevanceEntriesProvider(仅watchOS 26+)
Build a widget that only appears when conditions match. The system calls
to learn when the widget matters, then calls with
the matching configuration to get render data.
relevance()entry()swift
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。系统会调用了解Widget的相关时机,然后调用并传入匹配的配置以获取渲染数据。
relevance()entry()swift
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
}
}Time-Based Relevance
基于时间的相关性
Time clues tell the system a widget matters at or around a specific moment.
时间线索告知系统Widget在特定时刻或时间段内相关。
Single Date
单个日期
swift
RelevantContext.date(eventDate)swift
RelevantContext.date(eventDate)Date with Kind
带类型的日期
DateKind| Kind | Use |
|---|---|
| General time relevance |
| A scheduled event (meeting, flight) |
| Information relevant around a time (weather forecast) |
swift
RelevantContext.date(meetingStart, kind: .scheduled)DateKind| 类型 | 用途 |
|---|---|
| 通用时间相关性 |
| 已安排的事件(会议、航班) |
| 特定时间前后相关的信息(天气预报) |
swift
RelevantContext.date(meetingStart, kind: .scheduled)Date Range
日期范围
swift
// 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)swift
// 使用from/to
RelevantContext.date(from: startDate, to: endDate)
// 使用DateInterval
RelevantContext.date(interval: dateInterval, kind: .scheduled)
// 使用ClosedRange
RelevantContext.date(range: startDate...endDate, kind: .default)Location-Based Relevance
基于位置的相关性
Inferred Locations
推断位置
The system infers certain locations from a person's routine. No coordinates
needed.
swift
RelevantContext.location(inferred: .home)
RelevantContext.location(inferred: .work)
RelevantContext.location(inferred: .school)
RelevantContext.location(inferred: .commute)Requires location permission in both the app and widget extension.
系统会根据用户的日常活动推断某些位置,无需提供坐标。
swift
RelevantContext.location(inferred: .home)
RelevantContext.location(inferred: .work)
RelevantContext.location(inferred: .school)
RelevantContext.location(inferred: .commute)需要在主应用和Widget扩展中均获取位置权限。
Specific Region
�特定区域
swift
import CoreLocation
let region = CLCircularRegion(
center: CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090),
radius: 500,
identifier: "apple-park"
)
RelevantContext.location(region)swift
import CoreLocation
let region = CLCircularRegion(
center: CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090),
radius: 500,
identifier: "apple-park"
)
RelevantContext.location(region)Point-of-Interest Category (watchOS 26+)
兴趣点类别(仅watchOS 26+)
Indicate relevance near any location of a given category. Returns if the
category is unsupported.
nilswift
import MapKit
if let context = RelevantContext.location(category: .beach) {
// Widget is relevant whenever the person is near a beach
}指定在某类位置附近时Widget相关。如果类别不支持则返回。
nilswift
import MapKit
if let context = RelevantContext.location(category: .beach) {
// 用户在海滩附近时,Widget相关
}Fitness and Sleep Relevance
健身与睡眠相关性
Fitness
健身
swift
// Relevant when activity rings are incomplete
RelevantContext.fitness(.activityRingsIncomplete)
// Relevant during an active workout
RelevantContext.fitness(.workoutActive)Requires HealthKit workout/activity permission.
swift
// 活动环未完成时相关
RelevantContext.fitness(.activityRingsIncomplete)
// 进行中锻炼时相关
RelevantContext.fitness(.workoutActive)需要HealthKit workout/activity权限。
Sleep
睡眠
swift
// Relevant around bedtime
RelevantContext.sleep(.bedtime)
// Relevant around wakeup
RelevantContext.sleep(.wakeup)Requires HealthKit permission.
sleepAnalysisswift
// 就寝时间前后相关
RelevantContext.sleep(.bedtime)
// 起床时间前后相关
RelevantContext.sleep(.wakeup)需要HealthKit 权限。
sleepAnalysisHardware Relevance
硬件相关性
swift
// Relevant when headphones are connected
RelevantContext.hardware(headphones: .connected)No special permission required.
swift
// 连接耳机时相关
RelevantContext.hardware(headphones: .connected)无需特殊权限。
Combining Signals
组合信号
Return multiple values in the
array to make a widget relevant under several different conditions.
WidgetRelevanceAttributeWidgetRelevanceswift
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)
}Order matters. Return relevance attributes ordered by priority. The system
may use only a subset of the provided relevances.
在数组中返回多个值,使Widget在多种不同条件下都能相关。
WidgetRelevanceWidgetRelevanceAttributeswift
func relevance() async -> WidgetRelevance<MyIntent> {
var attributes: [WidgetRelevanceAttribute<MyIntent>] = []
// 早高峰通勤时相关
attributes.append(
WidgetRelevanceAttribute(
configuration: MyIntent(mode: .commute),
context: .location(inferred: .commute)
)
)
// 工作时相关
attributes.append(
WidgetRelevanceAttribute(
configuration: MyIntent(mode: .work),
context: .location(inferred: .work)
)
)
// 已安排事件前后相关
for event in upcomingEvents {
attributes.append(
WidgetRelevanceAttribute(
configuration: MyIntent(eventID: event.id),
context: .date(event.date, kind: .scheduled)
)
)
}
return WidgetRelevance(attributes)
}顺序很重要。请按优先级排序返回相关性属性,系统可能仅使用其中的子集。
Widget Integration
Widget集成
Relevant Widget with RelevanceConfiguration
带RelevanceConfiguration的相关性Widget
swift
@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")
}
}swift
@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")
}
}Associating with a Timeline Widget
与时间线Widget关联
When both a timeline widget and a relevant widget show the same data, use
to prevent duplicate cards. The system replaces the timeline
widget card with relevant widget cards when they are suggested.
associatedKindswift
RelevanceConfiguration(
kind: "com.example.relevant-events",
provider: MyRelevanceProvider()
) { entry in
EventWidgetView(entry: entry)
}
.associatedKind("com.example.timeline-events")当时间线Widget和相关性Widget展示相同数据时,使用避免卡片重复。当相关性Widget卡片被推荐时,系统会用它们替换时间线Widget卡片。
associatedKindswift
RelevanceConfiguration(
kind: "com.example.relevant-events",
provider: MyRelevanceProvider()
) { entry in
EventWidgetView(entry: entry)
}
.associatedKind("com.example.timeline-events")Grouping
分组
WidgetRelevanceGroupswift
// 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
)WidgetRelevanceGroupswift
// 退出默认的按应用分组,使每个卡片独立显示
WidgetRelevanceAttribute(
configuration: intent,
group: .ungrouped
)
// 命名分组——同一时间仅显示分组中的一个Widget
WidgetRelevanceAttribute(
configuration: intent,
group: .named("weather-alerts")
)
// 默认系统分组
WidgetRelevanceAttribute(
configuration: intent,
group: .automatic
)RelevantIntent (Timeline Provider Path)
RelevantIntent(时间线提供器方案)
When using a timeline provider, also update so the
system has relevance data between timeline refreshes.
RelevantIntentManagerswift
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)
}Call this whenever relevance data changes -- not only during timeline refreshes.
使用时间线提供器时,还需更新,以便系统在时间线刷新间隔之间也能获取到相关性数据。
RelevantIntentManagerswift
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)
}每当相关性数据发生变化时都应调用此方法——不仅是在时间线刷新期间。
Previewing Relevant Widgets
预览相关性Widget
Use Xcode previews to verify appearance without simulating real conditions.
swift
// Preview with sample entries
#Preview("Events", widget: MyRelevantWidget.self, relevanceEntries: {
[EventEntry(event: .surfing), EventEntry(event: .meditation)]
})
// Preview with relevance configurations
#Preview("Relevance", widget: MyRelevantWidget.self, relevance: {
WidgetRelevance([
WidgetRelevanceAttribute(configuration: MyIntent(event: .surfing),
context: .date(Date(), kind: .scheduled))
])
})
// Preview with the full provider
#Preview("Provider", widget: MyRelevantWidget.self,
relevanceProvider: MyRelevanceProvider())使用Xcode预览验证Widget外观,无需模拟真实条件。
swift
// 使用示例条目预览
#Preview("Events", widget: MyRelevantWidget.self, relevanceEntries: {
[EventEntry(event: .surfing), EventEntry(event: .meditation)]
})
// 使用相关性配置预览
#Preview("Relevance", widget: MyRelevantWidget.self, relevance: {
WidgetRelevance([
WidgetRelevanceAttribute(configuration: MyIntent(event: .surfing),
context: .date(Date(), kind: .scheduled))
])
})
// 使用完整提供器预览
#Preview("Provider", widget: MyRelevantWidget.self,
relevanceProvider: MyRelevanceProvider())Testing
测试
Enable WidgetKit Developer Mode in Settings > Developer on the watch to
bypass Smart Stack rotation limits during development.
在手表的“设置”>“开发者”中启用WidgetKit Developer Mode,可在开发期间绕过Smart Stack的轮换限制。
Common Mistakes
常见错误
- Ignoring return order. The system may only use a subset of relevance attributes. Return them sorted by priority (most important first).
- Missing permissions in widget extension. Location, fitness, and sleep clues require permission in both the app and the widget extension. If only the app has permission, the clues are silently ignored.
- Using RelevanceKit API expecting iOS behavior. The API compiles on all platforms but only has effect on watchOS.
- Duplicate Smart Stack cards. When offering both a timeline widget and a
relevant widget for the same data, use to prevent duplication.
.associatedKind(_:) - Forgetting placeholder and preview entries. requires both
RelevanceEntriesProviderand a preview branch inplaceholder(context:)whenentry(configuration:context:)is true.context.isPreview - Not calling . When using timeline providers, calling this only inside
updateRelevantIntentsmeans the system has stale relevance data between refreshes. Update whenever data changes.timeline() - Ignoring nil from . This factory returns an optional. Not all
location(category:)values are supported.MKPointOfInterestCategory
- 忽略返回顺序:系统可能仅使用部分相关性属性。请按优先级排序返回(最重要的在前)。
- Widget扩展缺少权限:位置、健身和睡眠线索需要在主应用和Widget扩展中均获取权限。如果仅主应用有权限,线索会被静默忽略。
- 在iOS上使用RelevanceKit API并期望生效:该API可在所有平台编译,但仅在watchOS上生效。
- Smart Stack卡片重复:当为相同数据同时提供时间线Widget和相关性Widget时,请使用避免重复。
.associatedKind(_:) - 忘记占位符和预览条目:需要同时实现
RelevanceEntriesProvider和在placeholder(context:)中处理entry(configuration:context:)为true的情况以返回预览数据。context.isPreview - 未调用:使用时间线提供器时,仅在
updateRelevantIntents中调用此方法会导致系统在时间线刷新间隔之间使用过时的相关性数据。数据变化时应立即更新。timeline() - 忽略返回的nil:该工厂方法返回可选值。并非所有
location(category:)值都受支持。MKPointOfInterestCategory
Review Checklist
审核检查清单
- is present alongside
import RelevanceKitimport WidgetKit - clues match the app's actual data model
RelevantContext - Relevance attributes are ordered by priority
- Permissions requested in both app target and widget extension for location/fitness/sleep clues
- implements
RelevanceEntriesProvider,entry, andplaceholderrelevance - handled in
context.isPreviewto return preview dataentry(configuration:context:) - used when a timeline widget and relevant widget show the same data
.associatedKind(_:) - called when data changes (timeline provider path)
RelevantIntentManager.updateRelevantIntents - nil return handled
location(category:) - WidgetKit Developer Mode used for testing
- Widget previews verify appearance across display sizes
- 已同时导入和
RelevanceKitWidgetKit - 线索与应用的实际数据模型匹配
RelevantContext - 相关性属性已按优先级排序
- 位置/健身/睡眠线索已在主应用和Widget扩展中均请求权限
- 已实现
RelevanceEntriesProvider、entry和placeholder方法relevance - 在中已处理
entry(configuration:context:)以返回预览数据context.isPreview - 当时间线Widget和相关性Widget展示相同数据时已使用
.associatedKind(_:) - 数据变化时已调用(时间线提供器方案)
RelevantIntentManager.updateRelevantIntents - 已处理返回nil的情况
location(category:) - 测试时已启用WidgetKit Developer Mode
- 已在不同显示尺寸下验证Widget预览外观
References
参考资料
- references/relevancekit-patterns.md -- extended patterns, full provider implementations, permission handling, and grouping strategies
- RelevanceKit documentation
- RelevantContext
- Increasing the visibility of widgets in Smart Stacks
- RelevanceConfiguration
- RelevanceEntriesProvider
- What's new in watchOS 26 (WWDC25 session 334)
- references/relevancekit-patterns.md——扩展模式、完整提供器实现、权限处理和分组策略
- RelevanceKit documentation
- RelevantContext
- Increasing the visibility of widgets in Smart Stacks
- RelevanceConfiguration
- RelevanceEntriesProvider
- What's new in watchOS 26 (WWDC25 session 334)