skills/relevancekit/SKILL.md
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.
npx skillsauth add dpearson2699/swift-ios-skills relevancekitInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
watchOS uses two mechanisms to determine widget relevance in the Smart Stack:
relevance() on an existing
AppIntentTimelineProvider to attach RelevantContext clues to timeline
entries. Available across platforms; only watchOS acts on the data.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).
| 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.
import RelevanceKit
import WidgetKit
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.
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.
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)
}
}
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
}
}
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 clues tell the system a widget matters at or around a specific moment.
RelevantContext.date(eventDate)
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)
// 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)
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.
import CoreLocation
let region = CLCircularRegion(
center: CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090),
radius: 500,
identifier: "apple-park"
)
RelevantContext.location(region)
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
}
// 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.
// Relevant around bedtime
RelevantContext.sleep(.bedtime)
// Relevant around wakeup
RelevantContext.sleep(.wakeup)
Requires HealthKit sleepAnalysis permission.
// Relevant when headphones are connected
RelevantContext.hardware(headphones: .connected)
No special permission required.
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.
@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")
}
}
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")
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
)
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.
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())
Enable WidgetKit Developer Mode in Settings > Developer on the watch to bypass Smart Stack rotation limits during development.
NSWidgetWantsLocation and checks isAuthorizedForWidgetUpdates..associatedKind(_:) to prevent
duplication.RelevanceEntriesProvider
requires both placeholder(context:) and a preview branch in
entry(configuration:context:) when context.isPreview is true.updateRelevantIntents. When using timeline providers,
calling this only inside timeline() means the system has stale relevance
data between refreshes. Update whenever data changes.location(category:). This factory returns an optional.
Not all MKPointOfInterestCategory values are supported.import RelevanceKit is present alongside import WidgetKitRelevantContext clues match the app's actual data modelNSWidgetWantsLocationCLLocationManager.isAuthorizedForWidgetUpdatesHKWorkoutType or activity-ring quantity types as appropriatesleepAnalysisRelevanceEntriesProvider implements entry, placeholder, and relevancecontext.isPreview handled in entry(configuration:context:) to return preview data.associatedKind(_:) used when a timeline widget and relevant widget show the same dataRelevantIntentManager.updateRelevantIntents called when data changes (timeline provider path)location(category:) nil return handleddevelopment
Implement, review, or improve data visualizations using Swift Charts. Use when building bar, line, area, point, pie, donut, or iOS 26 3D charts; when adding chart selection, scrolling, annotations, axes, scales, legends, or foregroundStyle grouping; when plotting functions with BarPlot, LinePlot, AreaPlot, PointPlot, Chart3D, or SurfacePlot; or when creating heat maps, Gantt charts, grouped bars, sparklines, threshold lines, or spatial visualizations.
data-ai
Select, implement, or migrate between app architecture patterns for Apple platform apps. Use when choosing between MV (Model-View with @Observable), MVVM, MVI, TCA (The Composable Architecture), Clean Architecture, VIPER, or Coordinator patterns; when evaluating architecture fit for a feature's complexity; when migrating from one pattern to another; or when reviewing whether an app's current architecture is appropriate. Scoped to Apple-platform patterns using Swift 6.3, SwiftUI, and UIKit.
development
Apply Swift API Design Guidelines to name, label, and document Swift APIs. Covers argument label rules (prepositional phrase rule, grammatical phrase rule, first-label omission), mutating/nonmutating pair naming (-ed/-ing participle pattern, form- prefix, sort/sorted, formUnion/union), side-effect naming (noun for pure, verb for mutating), documentation comment structure (summary by declaration kind, O(1) complexity rule), clarity at call site, role-based naming, protocol naming (-able/-ible/-ing), default arguments over method families, casing conventions, and terminology. Use when designing new Swift APIs, reviewing naming and argument labels, writing documentation comments, or refactoring for call site clarity.
development
Implement, review, or improve in-app purchases and subscriptions using StoreKit 2. Use when building paywalls with SubscriptionStoreView or ProductView, processing transactions with Product and Transaction APIs, verifying entitlements, handling purchase flows (consumable, non-consumable, auto-renewable), implementing offer codes or promotional/win-back/introductory offers, managing subscription status and renewal state, setting up StoreKit testing with configuration files, or integrating Family Sharing, Ask to Buy, refund handling, and billing retry logic.