relevancekit

relevancekit

热门

使用 RelevanceKit 提升 Apple Watch 上的小组件曝光率。适用于为 watchOS 小组件提供上下文相关性信号、声明基于时间或位置的相关性、组合多个相关性提供者、协助系统在 watchOS 26 上于恰当时机展示正确的小组件,或者在跨 RelevanceKit/WidgetKit/HealthKit/MapKit 的智能叠放(Smart Stack)作用域中进行边界路由调度。

967Star
49Fork
更新于 2026/7/31
SKILL.md
只读
名称
relevancekit
描述

使用 RelevanceKit 提升 Apple Watch 上的小组件曝光率。适用于为 watchOS 小组件提供上下文相关性信号、声明基于时间或位置的相关性、组合多个相关性提供者、协助系统在 watchOS 26 上于恰当时机展示正确的小组件,或者在跨 RelevanceKit/WidgetKit/HealthKit/MapKit 的智能叠放(Smart Stack)作用域中进行边界路由调度。

RelevanceKit

在 Apple Watch 智能叠放(Smart Stack)中提供设备端上下文线索,从而提升小组件的显示优先级与曝光率。RelevanceKit 会根据时间、位置、健身状态、作息时间或连接的硬件,告知系统小组件在什么时候具备相关性。适配 Swift 6.3 / watchOS 26+。

Beta 测试阶段提示:在对 RelevanceKit 的可用性或行为做出确定性结论前,请先重新核对 Apple 官方最新文档。

参阅 references/relevancekit-patterns.md 查看完整的相关小组件(relevant-widget)、时间线提供者(timeline provider)、分组、预览及权限配置模式。

目录

概览

watchOS 主要通过两种机制来确定小组件在智能叠放(Smart Stack)中的相关性:

  1. Timeline provider relevance(时间线提供者相关性) -- 在已有的 AppIntentTimelineProvider 上实现 relevance() 方法,将 RelevantContext 线索附加到时间线条目(timeline entries)中。跨平台可用;但仅 watchOS 会对这些数据做出响应。
  2. Relevant widget(相关小组件) -- 结合 RelevanceEntriesProvider 使用 RelevanceConfiguration,构建完全由相关性线索驱动的小组件。系统会为每个符合相关条件(relevant entry)的条目创建单独的智能叠放卡片。仅支持 watchOS 26+。

当小组件始终有数据展示且相关性仅作为补充辅助时,请选择时间线提供者(timeline provider);当小组件应该仅在条件匹配时才显示,或者需要同时出现多张卡片时(例如显示接下来的多个日历日程),请选择相关小组件(relevant widget)。

核心类型

类型 模块 职责
RelevantContext RelevanceKit 上下文线索(日期、位置、健身、睡眠、硬件)
WidgetRelevance WidgetKit 针对某种小组件类型(widget kind)的相关性属性集合
WidgetRelevanceAttribute WidgetKit 将小组件配置(widget configuration)与 RelevantContext 进行关联绑定
WidgetRelevanceGroup WidgetKit 控制智能叠放中的分组行为
RelevanceConfiguration WidgetKit 由相关性线索驱动的小组件配置(watchOS 26+)
RelevanceEntriesProvider WidgetKit 为基于相关性配置的小组件提供数据条目(watchOS 26+)
RelevanceEntry WidgetKit 渲染单个相关小组件卡片所需的数据(watchOS 26+)

RelevanceConfigurationRelevanceEntriesProviderRelevanceEntry 属于 WidgetKit API。仅当它们作为暴露 RelevanceKit 线索的 watchOS 相关小组件工作流的一部分时,才将它们保留在本 Skill 作用域内。

环境配置

导入模块

import RelevanceKit
import WidgetKit

平台可用性

RelevantContext 虽已支持跨平台声明(iOS 17+、watchOS 10+),但 RelevanceKit 功能仅在 watchOS 上实际生效。在其他平台上调用相关 API 不会产生任何效果。时间线提供者的 relevance() 在 iOS 18+、macOS 15+、visionOS 26+ 和 watchOS 11+ 上可用,便于共享 provider 代码。而 RelevanceConfigurationRelevanceEntriesProviderRelevanceEntry 仅适用于 watchOS 26+。

权限要求

某些相关性线索需要特定权限或许可配置:

相关性线索 必需权限
.location(inferred:) 宿主 App(Containing app)申请位置权限;小组件扩展声明 NSWidgetWantsLocation
.location(_:) (CLRegion) 宿主 App 申请位置权限;小组件扩展声明 NSWidgetWantsLocation
.location(category:) 宿主 App 申请位置权限;小组件扩展声明 NSWidgetWantsLocation
.fitness(.workoutActive) HealthKit 对 HKWorkoutType 的访问权限
.fitness(.activityRingsIncomplete) HealthKit 对 appleExerciseTimeappleMoveTimeappleStandTime 的访问权限
.sleep(_:) HealthKit 的 sleepAnalysis 权限
.hardware(headphones:) 无需权限
.date(...) 无需权限

请务必将位置权限说明字符串(Usage Description)添加到宿主 App 的 Info.plist 中,而不只是小组件扩展。在小组件代码中,在依赖位置线索之前先检查 CLLocationManager.isAuthorizedForWidgetUpdates。对于健身和睡眠线索,请在提供相关性的 App 及小组件扩展 Target 中启用 HealthKit 并申请精确的读取类型。

相关性提供者

方式一:带有相关性支持的时间线提供者

为已有的 AppIntentTimelineProvider 添加 relevance() 方法。这种方式可以在 iOS 和 watchOS 之间共享代码,同时为 watchOS 智能叠放注入智能调度能力。

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)
    }
}

方式二:RelevanceEntriesProvider (watchOS 26+)

构建一个仅在条件匹配时才显现的小组件。系统通过调用 relevance() 来了解小组件何时产生价值,随后用匹配的配置调用 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
    }
}

边界路由与职责分工

当某个功能混合使用了小组件、位置、体能训练和智能叠放相关性时,请保持 RelevanceKit 专注于 RelevantContextWidgetRelevanceAttribute、提供者的 relevance()RelevantIntentManager、相关小组件接管机制以及相关性线索的权限管理。
将时间线(timelines)、刷新预算(reload budgets)、组件系列(families)、视图渲染(rendering)、APNs 小组件推送、实时活动(Live Activities)和小组件控件(widget Controls)路由分发给 WidgetKit;
HKWorkoutSessionHKLiveWorkoutBuilderHKWorkoutRoute、数据查询、健身记录环/睡眠数据以及授权交互 UX 路由分发给 HealthKit;
MKLocalSearchMKLocalSearchCompleterMKDirections、地理编码、定位授权、区域(regions)、地理围栏(geofencing)和 POI 地点数据路由分发给 MapKit/CoreLocation。

基于时间的相关性

时间线索告知系统小组件在特定时刻或时间段内具备高相关度。

单一日期

RelevantContext.date(eventDate)

带类型的日期(Date with Kind)

DateKind 提供了关于时间相关性性质的补充提示:

类型(Kind) 适用场景
.default 通用时间相关性
.scheduled 预定日程/预定事件(会议、航班等)
.informational 某个时间点附近的相关信息(如天气预报)
RelevantContext.date(meetingStart, kind: .scheduled)

日期范围

// 使用 from/to
RelevantContext.date(from: startDate, to: endDate)

// 使用 DateInterval
RelevantContext.date(interval: dateInterval, kind: .scheduled)

// 使用 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)

兴趣点类别(Point-of-Interest Category,26.0+ SDK)

指示小组件在指定类别的任何地点附近均具备相关性。若类别不支持则返回 nil。虽然该工厂方法在 Apple 平台的 26.0+ SDK 中均可调用,但 RelevanceKit 线索仍然只在 watchOS 上影响智能叠放行为。

import MapKit

if let context = RelevantContext.location(category: .beach) {
    // 只要用户位于沙滩附近,小组件即具备相关性
}

健身与睡眠相关性

健身状态

// 健身记录环未填满时生效
RelevantContext.fitness(.activityRingsIncomplete)

// 进行体能训练(Active Workout)期间生效
RelevantContext.fitness(.workoutActive)

请应用 权限要求 中的精确健身权限映射。

睡眠状态

// 临近就寝时间时生效
RelevantContext.sleep(.bedtime)

// 临近起床时间时生效
RelevantContext.sleep(.wakeup)

请应用 权限要求 中的睡眠权限映射。

硬件相关性

// 耳机连接时生效
RelevantContext.hardware(headphones: .connected)

无需特殊权限。

组合多重信号

WidgetRelevance 数组中返回多个 WidgetRelevanceAttribute 值,可使小组件在多种不同条件下都能被判定为具备相关性。

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)
}

顺序至关重要。 返回的相关性属性请按优先级排序。系统可能只会采用所提供相关性集合的其中一部分子集。

小组件集成

使用 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 避免产生重复卡片。系统会替代...

<!-- truncated for translation batch; full body continues in source -->