relevancekit
dpearson2699/swift-ios-skills
Increase Apple Watch widget visibility using contextual relevance signals for time, location, fitness, and hardware.
What is relevancekit?
RelevanceKit provides on-device contextual clues that increase a widget's visibility in the Apple Watch Smart Stack by signaling when a widget is relevant based on time, location, fitness state, sleep schedule, or connected hardware. Use it when building watchOS 26+ widgets that should appear at the right moment in the Smart Stack, or when combining multiple relevance providers to help the system surface the most useful widget.
- Declare time-based relevance with date and DateKind hints (default, scheduled, informational)
- Provide location-based relevance using inferred location, CLRegion, or location categories
- Signal fitness and sleep relevance using HealthKit data (workout active, activity rings, sleep state)
- Detect connected hardware relevance (headphones, wearables)
- Combine multiple relevance signals into a single WidgetRelevance for Smart Stack prioritization
- Build relevant-widget configurations that only appear when conditions match (watchOS 26+)
How to install relevancekit
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill relevancekit- Swift 6.3 or later
- watchOS 26+ for RelevanceConfiguration and RelevanceEntriesProvider; watchOS 10+ for RelevantContext
- Location permission (NSWidgetWantsLocation) for location-based relevance
- HealthKit authorization for fitness and sleep relevance clues
- CoreLocation setup for location categories and regions
How to use relevancekit
- 1.Import RelevanceKit and WidgetKit in your widget extension
- 2.Choose between timeline provider relevance (add relevance() method) or RelevanceEntriesProvider (watchOS 26+ only)
- 3.Create RelevantContext clues matching your widget's conditions (date, location, fitness, sleep, or hardware)
- 4.Wrap each configuration with WidgetRelevanceAttribute pairing the intent with its context
- 5.Return a WidgetRelevance collection from your relevance() method
- 6.For relevant widgets, implement entry() to provide render data for matched configurations
- 7.Verify permissions are granted (location, HealthKit) before relying on those clues
- 8.Test on watchOS device or simulator to confirm Smart Stack visibility behavior
Use cases
- Show a workout widget only when an active workout is detected
- Display calendar events in the Smart Stack at their scheduled times with DateKind.scheduled
- Surface a location-based widget when the user enters a specific region
- Show multiple upcoming event cards simultaneously using RelevanceEntriesProvider
- Combine time and location signals to display a weather widget when relevant to the user's schedule and location
- watchOS app developers building Smart Stack widgets
- iOS developers sharing AppIntentTimelineProvider code across platforms
- Fitness and health app developers integrating HealthKit relevance signals
- Location-aware app developers using CoreLocation with widgets
relevancekit FAQ
Timeline provider relevance adds relevance clues to an existing AppIntentTimelineProvider, sharing code across platforms while adding watchOS Smart Stack intelligence. RelevanceEntriesProvider (watchOS 26+ only) builds a widget that only appears when conditions match, allowing multiple cards per configuration. Use timeline provider when the widget always has data; use RelevanceEntriesProvider when the widget should only appear conditionally.
RelevantContext is declared across platforms (iOS 17+, watchOS 10+), but RelevanceKit functionality only takes effect on watchOS. Timeline-provider relevance() is available on iOS 18+, macOS 15+, visionOS 26+, and watchOS 11+. RelevanceConfiguration and RelevanceEntriesProvider are watchOS 26+ only.
The containing app must request location access, and the widget extension must declare NSWidgetWantsLocation in its Info.plist. Check CLLocationManager.isAuthorizedForWidgetUpdates before relying on location clues in widget code.
Yes. Create separate RelevantContext clues for each signal and wrap them in WidgetRelevanceAttribute with the same configuration. Return all attributes in a single WidgetRelevance collection; the system combines them to determine overall relevance.
RelevanceKit targets watchOS 26+, which is in beta. Re-check Apple documentation before making strong availability or behavior claims, as APIs and behavior may change.
Full instructions (SKILL.md)
Source of truth, from dpearson2699/swift-ios-skills.
name: relevancekit description: "Increase widget visibility on Apple Watch using RelevanceKit. Use when providing contextual relevance signals for watchOS widgets, declaring time-based or location-based relevance, combining multiple relevance providers, helping the system surface the right widget at the right time on watchOS 26, or routing mixed RelevanceKit/WidgetKit/HealthKit/MapKit Smart Stack scope."
RelevanceKit
Provide on-device contextual clues that increase a widget's visibility in the Apple Watch Smart Stack. RelevanceKit tells the system when a widget is relevant by time, location, fitness state, sleep schedule, or connected hardware. Targets Swift 6.3 / watchOS 26+.
Beta-sensitive. Re-check Apple documentation before making strong RelevanceKit availability or behavior claims.
See references/relevancekit-patterns.md for complete relevant-widget, timeline provider, grouping, preview, and permission patterns.
Contents
- Overview
- Setup
- Relevance Providers
- Boundary Routing
- Time-Based Relevance
- Location-Based Relevance
- Fitness and Sleep Relevance
- Hardware Relevance
- Combining Signals
- Widget Integration
- Common Mistakes
- Review Checklist
- References
Overview
watchOS uses two mechanisms to determine widget relevance in the Smart Stack:
- Timeline provider relevance -- implement
relevance()on an existingAppIntentTimelineProviderto attachRelevantContextclues to timeline entries. Available across platforms; only watchOS acts on the data. - Relevant widget -- use
RelevanceConfigurationwith aRelevanceEntriesProviderto 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).
Key Types
| Type | Module | Role |
|---|---|---|
RelevantContext | RelevanceKit | A contextual clue (date, location, fitness, sleep, hardware) |
WidgetRelevance | WidgetKit | Collection of relevance attributes for a widget kind |
WidgetRelevanceAttribute | WidgetKit | Pairs a widget configuration with a RelevantContext |
WidgetRelevanceGroup | WidgetKit | Controls grouping behavior in the Smart Stack |
RelevanceConfiguration | WidgetKit | Widget configuration driven by relevance clues (watchOS 26+) |
RelevanceEntriesProvider | WidgetKit | Provides entries for a relevance-configured widget (watchOS 26+) |
RelevanceEntry | WidgetKit | Data needed to render one relevant widget card (watchOS 26+) |
RelevanceConfiguration, RelevanceEntriesProvider, and RelevanceEntry are
WidgetKit APIs. Keep them in this skill's scope only when they are part of the
watchOS relevant-widget workflow that exposes RelevanceKit clues.
Setup
Import
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. Timeline-provider relevance() is available on
iOS 18+, macOS 15+, visionOS 26+, and watchOS 11+ for shared provider code.
RelevanceConfiguration, RelevanceEntriesProvider, and RelevanceEntry are
watchOS 26+ only.
Permissions
Certain relevance clues require authorization or target setup:
| Clue | Required Permission |
|---|---|
.location(inferred:) | Containing app requests location access; widget extension declares NSWidgetWantsLocation |
.location(_:) (CLRegion) | Containing app requests location access; widget extension declares NSWidgetWantsLocation |
.location(category:) | Containing app requests location access; widget extension declares NSWidgetWantsLocation |
.fitness(.workoutActive) | HealthKit access to HKWorkoutType |
.fitness(.activityRingsIncomplete) | HealthKit access to appleExerciseTime, appleMoveTime, and appleStandTime |
.sleep(_:) | HealthKit sleepAnalysis permission |
.hardware(headphones:) | None |
.date(...) | None |
Add location purpose strings to the containing app's Info.plist, not only the
widget extension. In widget code, check CLLocationManager.isAuthorizedForWidgetUpdates
before relying on location clues. For fitness and sleep clues, enable HealthKit
and request the exact read types in the app and widget extension target that
provides relevance.
Relevance Providers
Option 1: Timeline Provider with Relevance
Add a relevance() method to an existing AppIntentTimelineProvider. This
approach shares code across iOS and watchOS while adding watchOS Smart Stack
intelligence.
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+)
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.
@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
}
}
Boundary Routing
When a feature mixes widgets, location, workouts, and Smart Stack relevance,
keep RelevanceKit focused on RelevantContext, WidgetRelevanceAttribute,
provider relevance(), RelevantIntentManager, relevant-widget handoffs, and
permissions for relevance clues. Route timelines, reload budgets, families,
rendering, APNs widget pushes, Live Activities, and widget Controls to
WidgetKit; HKWorkoutSession, HKLiveWorkoutBuilder, HKWorkoutRoute,
queries, activity-ring/sleep data, and authorization UX to HealthKit; and
MKLocalSearch, MKLocalSearchCompleter, MKDirections, geocoding,
authorization, regions, geofencing, and place data to MapKit/CoreLocation.
Time-Based Relevance
Time clues tell the system a widget matters at or around a specific moment.
Single Date
RelevantContext.date(eventDate)
Date with Kind
DateKind provides an additional hint about the nature of the time relevance:
| Kind | Use |
|---|---|
.default | General time relevance |
.scheduled | A scheduled event (meeting, flight) |
.informational | Information relevant around a time (weather forecast) |
RelevantContext.date(meetingStart, kind: .scheduled)
Date Range
// 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)
Location-Based Relevance
Inferred Locations
The system infers certain locations from a person's routine. No coordinates needed.
RelevantContext.location(inferred: .home)
RelevantContext.location(inferred: .work)
RelevantContext.location(inferred: .school)
RelevantContext.location(inferred: .commute)
Requires app location authorization plus NSWidgetWantsLocation in the widget
extension.
Specific Region
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+ SDKs)
Indicate relevance near any location of a given category. Returns nil if the
category is unsupported. The factory is SDK-available on Apple platforms 26.0+,
but RelevanceKit clues still only affect Smart Stack behavior on watchOS.
import MapKit
if let context = RelevantContext.location(category: .beach) {
// Widget is relevant whenever the person is near a beach
}
Fitness and Sleep Relevance
Fitness
// Relevant when activity rings are incomplete
RelevantContext.fitness(.activityRingsIncomplete)
// Relevant during an active workout
RelevantContext.fitness(.workoutActive)
Requires the specific HealthKit read types for the clue: HKWorkoutType for
.workoutActive; appleExerciseTime, appleMoveTime, and appleStandTime
for .activityRingsIncomplete.
Sleep
// Relevant around bedtime
RelevantContext.sleep(.bedtime)
// Relevant around wakeup
RelevantContext.sleep(.wakeup)
Requires HealthKit sleepAnalysis permission.
Hardware Relevance
// Relevant when headphones are connected
RelevantContext.hardware(headphones: .connected)
No special permission required.
Combining Signals
Return multiple WidgetRelevanceAttribute values in the WidgetRelevance
array to make a widget relevant under several different conditions.
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 Integration
Relevant Widget with 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")
}
}
Associating with a Timeline 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.
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.
// 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 (Timeline Provider Path)
When using a timeline provider, also update RelevantIntentManager so the
system has relevance data between timeline refreshes.
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.
Previewing Relevant Widgets
Use Xcode previews to verify appearance without simulating real conditions.
// 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())
Testing
Enable WidgetKit Developer Mode in Settings > Developer on the watch to bypass Smart Stack rotation limits during development.
Common Mistakes
- Ignoring return order. The system may only use a subset of relevance attributes. Return them sorted by priority (most important first).
- Mixing app and widget location setup. The containing app requests location
authorization and owns the purpose strings; the widget extension declares
NSWidgetWantsLocationand checksisAuthorizedForWidgetUpdates. - Using generic HealthKit permission for fitness clues. Request the exact HealthKit types required by the clue instead of a broad "activity" permission.
- 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.
RelevanceEntriesProviderrequires bothplaceholder(context:)and a preview branch inentry(configuration:context:)whencontext.isPreviewis true. - Not calling
updateRelevantIntents. When using timeline providers, calling this only insidetimeline()means the system has stale relevance data between refreshes. Update whenever data changes. - Ignoring nil from
location(category:). This factory returns an optional. Not allMKPointOfInterestCategoryvalues are supported.
Review Checklist
-
import RelevanceKitis present alongsideimport WidgetKit -
RelevantContextclues match the app's actual data model - Relevance attributes are ordered by priority
- Location clues: app has purpose strings and authorization flow; widget extension has
NSWidgetWantsLocation - Widget location code checks
CLLocationManager.isAuthorizedForWidgetUpdates - Fitness clues request
HKWorkoutTypeor activity-ring quantity types as appropriate - Sleep clues request HealthKit
sleepAnalysis -
RelevanceEntriesProviderimplementsentry,placeholder, andrelevance -
context.isPreviewhandled inentry(configuration:context:)to return preview data -
.associatedKind(_:)used when a timeline widget and relevant widget show the same data -
RelevantIntentManager.updateRelevantIntentscalled when data changes (timeline provider path) -
location(category:)nil return handled - Mixed-framework plans keep WidgetKit, HealthKit, and MapKit/CoreLocation implementation details in sibling-skill scope
- WidgetKit Developer Mode used for testing
- Widget previews verify appearance across display sizes
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)
Related skills
More from dpearson2699/swift-ios-skills and the wider catalog.

scenekit
Maintain and extend existing SceneKit 3D scenes; soft-deprecated, use for legacy apps only.

sensorkit
Access research-grade sensor data from iOS/watchOS for Apple-approved studies.

shareplay-activities
Build shared real-time experiences with GroupActivities and SharePlay on iOS, macOS, tvOS, and visionOS.

speech-recognition
Transcribe speech to text on iOS using Apple's Speech framework with live microphone and file support.

spritekit
Build 2D games and animations with SpriteKit for iOS using scenes, sprites, physics, and SwiftUI integration.

storekit
Implement in-app purchases and subscriptions using StoreKit 2 on iOS 16+.