relevancekit

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.

Skill file

Preview skill file
---
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](references/relevancekit-patterns.md) for complete relevant-widget, timeline provider, grouping, preview, and permission patterns.

## Contents

- [Overview](#overview)
- [Setup](#setup)
- [Relevance Providers](#relevance-providers)
- [Boundary Routing](#boundary-routing)
- [Time-Based Relevance](#time-based-relevance)
- [Location-Based Relevance](#location-based-relevance)
- [Fitness and Sleep Relevance](#fitness-and-sleep-relevance)
- [Hardware Relevance](#hardware-relevance)
- [Combining Signals](#combining-signals)
- [Widget Integration](#widget-integration)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)

## 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).

### 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

```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. 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.

```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+)

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
@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

```swift
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) |

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

## 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 app location authorization plus `NSWidgetWantsLocation` in the widget
extension.

### Specific 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 (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.

```swift
import MapKit

if let context = RelevantContext.location(category: .beach) {
    // Widget is relevant whenever the person is near a beach
}
```

## Fitness and Sleep Relevance

### Fitness

```swift
// 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

```swift
// Relevant around bedtime
RelevantContext.sleep(.bedtime)

// Relevant around wakeup
RelevantContext.sleep(.wakeup)
```

Requires HealthKit `sleepAnalysis` permission.

## Hardware Relevance

```swift
// 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.

```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.

## Widget Integration

### Relevant Widget with RelevanceConfiguration

```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

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")
```

### 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
)
```

### RelevantIntent (Timeline Provider Path)

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.

### Previewing Relevant Widgets

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())
```

### 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
  `NSWidgetWantsLocation` and checks `isAuthorizedForWidgetUpdates`.
- **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.** `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.

## Review Checklist

- [ ] `import RelevanceKit` is present alongside `import WidgetKit`
- [ ] `RelevantContext` clues 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 `HKWorkoutType` or activity-ring quantity types as appropriate
- [ ] Sleep clues request HealthKit `sleepAnalysis`
- [ ] `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
- [ ] 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](references/relevancekit-patterns.md) -- extended patterns, full provider
  implementations, permission handling, and grouping strategies
- [RelevanceKit documentation](https://sosumi.ai/documentation/relevancekit)
- [RelevantContext](https://sosumi.ai/documentation/relevancekit/relevantcontext)
- [Increasing the visibility of widgets in Smart Stacks](https://sosumi.ai/documentation/widgetkit/widget-suggestions-in-smart-stacks)
- [RelevanceConfiguration](https://sosumi.ai/documentation/widgetkit/relevanceconfiguration)
- [RelevanceEntriesProvider](https://sosumi.ai/documentation/widgetkit/relevanceentriesprovider)
- [What's new in watchOS 26 (WWDC25 session 334)](https://sosumi.ai/videos/play/wwdc2025/334/)

Source

Creator's repository · dpearson2699/swift-ios-skills

View on GitHub

Security

Security checks in progress
Results will appear here once audits complete
What this skill can do
Reads your filesConnects to the internetRuns code on your machine
Checked by 3 independent security firms
Does it try to trick the AI?Not yet checkedPending · Gen Agent Trust Hub
Does it sneak in hidden code?Not yet checkedPending · Socket
Does it have known bugs?Not yet checkedPending · Snyk