relevancekit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

RelevanceKit

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:
  1. Timeline provider relevance -- implement
    relevance()
    on an existing
    AppIntentTimelineProvider
    to attach
    RelevantContext
    clues to timeline entries. Available across platforms; only watchOS acts on the data.
  2. Relevant widget -- use
    RelevanceConfiguration
    with a
    RelevanceEntriesProvider
    to build a widget driven entirely by relevance clues. The system creates individual Smart Stack cards per relevant entry. watchOS 26+ only.
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的相关性:
  1. 时间线提供器相关性——在现有的
    AppIntentTimelineProvider
    上实现
    relevance()
    方法,将
    RelevantContext
    线索附加到时间线条目上。该API支持多平台,但仅watchOS会实际使用这些数据。
  2. 相关性Widget——结合
    RelevanceConfiguration
    RelevanceEntriesProvider
    构建完全由相关性线索驱动的Widget。系统会为每个相关条目创建独立的Smart Stack卡片。仅适用于watchOS 26+。
如果Widget始终有数据展示,且相关性仅作为补充功能,选择时间线提供器方案。如果Widget仅需在满足特定条件时显示,或需要同时展示多张卡片(例如多个即将到来的日历事件),则选择相关性Widget方案。

Key Types

核心类型

TypeModuleRole
RelevantContext
RelevanceKitA contextual clue (date, location, fitness, sleep, hardware)
WidgetRelevance
WidgetKitCollection of relevance attributes for a widget kind
WidgetRelevanceAttribute
WidgetKitPairs a widget configuration with a
RelevantContext
WidgetRelevanceGroup
WidgetKitControls grouping behavior in the Smart Stack
RelevanceConfiguration
WidgetKitWidget configuration driven by relevance clues (watchOS 26+)
RelevanceEntriesProvider
WidgetKitProvides entries for a relevance-configured widget (watchOS 26+)
RelevanceEntry
WidgetKitData needed to render one relevant widget card (watchOS 26+)
类型模块作用
RelevantContext
RelevanceKit上下文线索(日期、位置、健身、睡眠、硬件)
WidgetRelevance
WidgetKit某类Widget的相关性属性集合
WidgetRelevanceAttribute
WidgetKit将Widget配置与
RelevantContext
配对
WidgetRelevanceGroup
WidgetKit控制Smart Stack中的分组行为
RelevanceConfiguration
WidgetKit由相关性线索驱动的Widget配置(仅watchOS 26+)
RelevanceEntriesProvider
WidgetKit为相关性配置的Widget提供条目数据(仅watchOS 26+)
RelevanceEntry
WidgetKit渲染单个相关性Widget卡片所需的数据(仅watchOS 26+)

Setup

设置

Import

导入模块

swift
import RelevanceKit
import WidgetKit
swift
import RelevanceKit
import WidgetKit

Platform Availability

平台可用性

RelevantContext
is declared across platforms (iOS 17+, watchOS 10+), but RelevanceKit functionality only takes effect on watchOS. Calling the API on other platforms has no effect.
RelevanceConfiguration
,
RelevanceEntriesProvider
, and
RelevanceEntry
are watchOS 26+ only.
RelevantContext
在多平台上声明(iOS 17+、watchOS 10+),但RelevanceKit的功能仅在watchOS上生效。在其他平台调用该API不会产生任何效果。
RelevanceConfiguration
RelevanceEntriesProvider
RelevanceEntry
仅适用于watchOS 26+。

Permissions

权限配置

Certain relevance clues require the user to grant permission to both the app and the widget extension:
ClueRequired Permission
.location(inferred:)
Location access
.location(_:)
(CLRegion)
Location access
.location(category:)
Location access
.fitness(_:)
HealthKit workout/activity rings permission
.sleep(_:)
HealthKit
sleepAnalysis
permission
.hardware(headphones:)
None
.date(...)
None
Request permissions in both the main app target and the widget extension target.
某些相关性线索需要用户同时向主应用和Widget扩展授予权限:
线索类型所需权限
.location(inferred:)
位置访问权限
.location(_:)
(CLRegion)
位置访问权限
.location(category:)
位置访问权限
.fitness(_:)
HealthKit workout/activity rings权限
.sleep(_:)
HealthKit
sleepAnalysis
权限
.hardware(headphones:)
.date(...)
请在主应用目标和Widget扩展目标中均请求相应权限。

Relevance Providers

相关性提供器

Option 1: Timeline Provider with Relevance

方案1:带相关性的时间线提供器

Add a
relevance()
method to an existing
AppIntentTimelineProvider
. This approach shares code across iOS and watchOS while adding watchOS Smart Stack intelligence.
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)
    }
}
在现有的
AppIntentTimelineProvider
中添加
relevance()
方法。该方案可在iOS和watchOS间共享代码,同时为watchOS的Smart Stack增加智能展示能力。
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
relevance()
to learn when the widget matters, then calls
entry()
with the matching configuration to get render data.
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。系统会调用
relevance()
了解Widget的相关时机,然后调用
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
provides an additional hint about the nature of the time relevance:
KindUse
.default
General time relevance
.scheduled
A scheduled event (meeting, flight)
.informational
Information relevant around a time (weather forecast)
swift
RelevantContext.date(meetingStart, kind: .scheduled)
DateKind
为时间相关性提供额外的提示:
类型用途
.default
通用时间相关性
.scheduled
已安排的事件(会议、航班)
.informational
特定时间前后相关的信息(天气预报)
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
nil
if the category is unsupported.
swift
import MapKit

if let context = RelevantContext.location(category: .beach) {
    // Widget is relevant whenever the person is near a beach
}
指定在某类位置附近时Widget相关。如果类别不支持则返回
nil
swift
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
sleepAnalysis
permission.
swift
// 就寝时间前后相关
RelevantContext.sleep(.bedtime)

// 起床时间前后相关
RelevantContext.sleep(.wakeup)
需要HealthKit
sleepAnalysis
权限。

Hardware Relevance

硬件相关性

swift
// Relevant when headphones are connected
RelevantContext.hardware(headphones: .connected)
No special permission required.
swift
// 连接耳机时相关
RelevantContext.hardware(headphones: .connected)
无需特殊权限。

Combining Signals

组合信号

Return multiple
WidgetRelevanceAttribute
values in the
WidgetRelevance
array to make a widget relevant under several different conditions.
swift
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.
WidgetRelevance
数组中返回多个
WidgetRelevanceAttribute
值,使Widget在多种不同条件下都能相关。
swift
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
associatedKind
to prevent duplicate cards. The system replaces the timeline widget card with relevant widget cards when they are suggested.
swift
RelevanceConfiguration(
    kind: "com.example.relevant-events",
    provider: MyRelevanceProvider()
) { entry in
    EventWidgetView(entry: entry)
}
.associatedKind("com.example.timeline-events")
当时间线Widget和相关性Widget展示相同数据时,使用
associatedKind
避免卡片重复。当相关性Widget卡片被推荐时,系统会用它们替换时间线Widget卡片。
swift
RelevanceConfiguration(
    kind: "com.example.relevant-events",
    provider: MyRelevanceProvider()
) { entry in
    EventWidgetView(entry: entry)
}
.associatedKind("com.example.timeline-events")

Grouping

分组

WidgetRelevanceGroup
controls how the system groups widgets in the Smart Stack.
swift
// 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
)
WidgetRelevanceGroup
控制系统在Smart Stack中对Widget的分组方式。
swift
// 退出默认的按应用分组,使每个卡片独立显示
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
RelevantIntentManager
so the system has relevance data between timeline refreshes.
swift
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.
使用时间线提供器时,还需更新
RelevantIntentManager
,以便系统在时间线刷新间隔之间也能获取到相关性数据。
swift
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
    .associatedKind(_:)
    to prevent duplication.
  • Forgetting placeholder and preview entries.
    RelevanceEntriesProvider
    requires both
    placeholder(context:)
    and a preview branch in
    entry(configuration:context:)
    when
    context.isPreview
    is true.
  • Not calling
    updateRelevantIntents
    .
    When using timeline providers, calling this only inside
    timeline()
    means the system has stale relevance data between refreshes. Update whenever data changes.
  • Ignoring nil from
    location(category:)
    .
    This factory returns an optional. Not all
    MKPointOfInterestCategory
    values are supported.
  • 忽略返回顺序:系统可能仅使用部分相关性属性。请按优先级排序返回(最重要的在前)。
  • Widget扩展缺少权限:位置、健身和睡眠线索需要在主应用和Widget扩展中均获取权限。如果仅主应用有权限,线索会被静默忽略。
  • 在iOS上使用RelevanceKit API并期望生效:该API可在所有平台编译,但仅在watchOS上生效。
  • Smart Stack卡片重复:当为相同数据同时提供时间线Widget和相关性Widget时,请使用
    .associatedKind(_:)
    避免重复。
  • 忘记占位符和预览条目
    RelevanceEntriesProvider
    需要同时实现
    placeholder(context:)
    和在
    entry(configuration:context:)
    中处理
    context.isPreview
    为true的情况以返回预览数据。
  • 未调用
    updateRelevantIntents
    :使用时间线提供器时,仅在
    timeline()
    中调用此方法会导致系统在时间线刷新间隔之间使用过时的相关性数据。数据变化时应立即更新。
  • 忽略
    location(category:)
    返回的nil
    :该工厂方法返回可选值。并非所有
    MKPointOfInterestCategory
    值都受支持。

Review Checklist

审核检查清单

  • import RelevanceKit
    is present alongside
    import WidgetKit
  • RelevantContext
    clues match the app's actual data model
  • Relevance attributes are ordered by priority
  • Permissions requested in both app target and widget extension for location/fitness/sleep clues
  • RelevanceEntriesProvider
    implements
    entry
    ,
    placeholder
    , and
    relevance
  • context.isPreview
    handled in
    entry(configuration:context:)
    to return preview data
  • .associatedKind(_:)
    used when a timeline widget and relevant widget show the same data
  • RelevantIntentManager.updateRelevantIntents
    called when data changes (timeline provider path)
  • location(category:)
    nil return handled
  • WidgetKit Developer Mode used for testing
  • Widget previews verify appearance across display sizes
  • 已同时导入
    RelevanceKit
    WidgetKit
  • RelevantContext
    线索与应用的实际数据模型匹配
  • 相关性属性已按优先级排序
  • 位置/健身/睡眠线索已在主应用和Widget扩展中均请求权限
  • RelevanceEntriesProvider
    已实现
    entry
    placeholder
    relevance
    方法
  • entry(configuration:context:)
    中已处理
    context.isPreview
    以返回预览数据
  • 当时间线Widget和相关性Widget展示相同数据时已使用
    .associatedKind(_:)
  • 数据变化时已调用
    RelevantIntentManager.updateRelevantIntents
    (时间线提供器方案)
  • 已处理
    location(category:)
    返回nil的情况
  • 测试时已启用WidgetKit Developer Mode
  • 已在不同显示尺寸下验证Widget预览外观

References

参考资料