mcpbeat

Relevancekit

dpearson2699/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.

11k tokens
context cost
the whole folder, loaded on every use
3
files
instructions only
0
copies elsewhere
how many repositories repackaged it
960
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill relevancekit

The instruction itself

34 sections, as written by the author

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 existing

AppIntentTimelineProvider to attach RelevantContext clues to timeline

entries. Available across platforms; only watchOS acts on the data.

  • 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

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)

Apply the location row in the Permissions table and check

CLLocationManager.isAuthorizedForWidgetUpdates before returning clues.

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)

Apply the exact fitness mapping in Permissions.

Sleep

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

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

Apply the sleep mapping in Permissions.

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 the entry, relevance-configuration, and full-provider recipes in

Preview Recipes. Enable

WidgetKit Developer Mode on the watch, test permissions granted and denied, and

finish on a physical Apple Watch; see

Testing Tips.

Common Mistakes

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

  • 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

  • [ ] Routing: RelevanceKit is watchOS-effect-only, and WidgetKit,

HealthKit, MapKit, and CoreLocation implementation remains in sibling scope.

  • [ ] Signals: contexts match the data model, attributes are priority-ordered,

and every clue uses the exact Permissions-table setup.

  • [ ] Providers: entry, placeholder, relevance, and preview paths are present;

location category optionals and widget-update authorization are handled.

  • [ ] Coordination: .associatedKind(_:) prevents duplicate cards, and

updateRelevantIntents runs whenever timeline-provider data changes.

  • [ ] Testing: Developer Mode is enabled and previews cover display sizes plus

granted and denied permission states.

References

  • references/relevancekit-patterns.md -- extended patterns, full provider

implementations, permission handling, and grouping strategies

How to use it

Copy the folder

Take dpearson2699/relevancekit from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.