mcpField
R
relevancekit

relevancekit

github.com/dpearson2699开发1.2k3.1k 次安装13 周前更新
代码仓库

关于 relevancekit

relevancekit 是面向 Apple Watch 小组件开发者的指南,用相关性线索影响 Smart Stack 中小组件的显示时机。它介绍时间、位置、健身、睡眠和硬件状态等线索及其权限要求。

主要功能

  1. 01按时间、位置和活动设置相关性
  2. 02构建由相关性驱动的小组件
  3. 03组合并排序多个相关性线索
  4. 04配置小组件分组与关联类型
  5. 05检查位置、健身和睡眠权限

适用场景

  1. 01在活动开始前显示日程小组件
  2. 02在通勤或特定地点提示相关信息
  3. 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 使用两种机制来确定小组件在智能叠放中的相关性:

  1. 时间线提供程序相关性 -- 在现有 AppIntentTimelineProvider 上实现 relevance(),为时间线条目附加 RelevantContext 线索。此功能可用于多个平台;只有 watchOS 会根据这些数据采取行动。
  2. 相关小组件 -- 使用 RelevanceConfiguration 和 RelevanceEntriesProvider 构建完全由相关性线索驱动的小组件。仅适用于 watchOS 26+。系统会为每个相关条目创建单独的智能叠放卡片。

如果小组件始终有数据要显示,且相关性只是辅助信息,请选择时间线提供程序。如果小组件只应在条件匹配时出现,或需要同时显示多张卡片(例如多个即将开始的日历事件),请选用相关小组件。

关键类型
类型模块作用
RelevantContextRelevanceKit上下文线索(日期、位置、健身、睡眠、硬件)
WidgetRelevanceWidgetKit某种小组件类型的相关性属性集合
WidgetRelevanceAttributeWidgetKit将小组件配置与 RelevantContext 配对
WidgetRelevanceGroupWidgetKit控制智能叠放中的分组行为
RelevanceConfigurationWidgetKit由相关性线索驱动的小组件配置(watchOS 26+)
RelevanceEntriesProviderWidgetKit为采用相关性配置的小组件提供条目(watchOS 26+)
RelevanceEntryWidgetKit渲染一张相关小组件卡片所需的数据(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。
  • 测试:已启用开发者模式,且预览涵盖不同显示尺寸以及 已授予和已拒绝权限的状态。

参考资料

评论

支持 Markdown