home / skills / rshankras / claude-code-apple-skills / watchos

watchos skill

/skills/watchos

This skill guides watchOS development with SwiftUI, connectivity, and complications to optimize app design, performance, and user experience.

npx playbooks add skill rshankras/claude-code-apple-skills --skill watchos

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
7.2 KB
---
name: watchOS
description: watchOS development guidance including SwiftUI for Watch, Watch Connectivity, complications, and watch-specific UI patterns. Use for watchOS code review, best practices, or Watch app development.
allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion]
---

# watchOS Development

Comprehensive guidance for watchOS app development with SwiftUI, Watch Connectivity, and complications.

## When This Skill Activates

Use this skill when the user:
- Is building a watchOS app or Watch extension
- Asks about Watch Connectivity (iPhone ↔ Watch sync)
- Needs help with complications or ClockKit
- Wants to implement watch-specific UI patterns

## Key Principles

### 1. Watch-First Design
- Glanceable content - users look for seconds, not minutes
- Quick interactions - 2 seconds or less
- Essential information only - no scrolling walls of text
- Large touch targets - minimum 38pt height

### 2. Independent vs Companion
- Prefer independent Watch apps when possible
- Use Watch Connectivity for data sync, not as dependency
- Cache data locally for offline access
- Handle connectivity failures gracefully

### 3. Performance
- Minimize background work (battery)
- Use complication updates sparingly
- Prefer timeline-based content over live updates
- Keep views lightweight

## Architecture Patterns

### App Structure

```swift
@main
struct MyWatchApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

### Navigation

```swift
// Use NavigationStack (watchOS 9+)
NavigationStack {
    List {
        NavigationLink("Item 1", value: Item.one)
        NavigationLink("Item 2", value: Item.two)
    }
    .navigationDestination(for: Item.self) { item in
        ItemDetailView(item: item)
    }
}

// TabView for main sections
TabView {
    HomeView()
    ActivityView()
    SettingsView()
}
.tabViewStyle(.verticalPage)
```

### List Design

```swift
List {
    ForEach(items) { item in
        ItemRow(item: item)
    }
    .onDelete(perform: delete)
}
.listStyle(.carousel)  // For focused content
.listStyle(.elliptical)  // For browsing
```

## Watch Connectivity

### Session Setup

```swift
import WatchConnectivity

@Observable
final class WatchConnectivityManager: NSObject, WCSessionDelegate {
    static let shared = WatchConnectivityManager()

    private(set) var isReachable = false

    override init() {
        super.init()
        if WCSession.isSupported() {
            WCSession.default.delegate = self
            WCSession.default.activate()
        }
    }

    // Required delegate methods
    func session(_ session: WCSession, activationDidCompleteWith state: WCSessionActivationState, error: Error?) {
        isReachable = session.isReachable
    }

    #if os(iOS)
    func sessionDidBecomeInactive(_ session: WCSession) {}
    func sessionDidDeactivate(_ session: WCSession) {
        WCSession.default.activate()
    }
    #endif
}
```

### Data Transfer Methods

| Method | Use Case | Delivery |
|--------|----------|----------|
| `updateApplicationContext` | Latest state (settings) | Overwrites previous |
| `sendMessage` | Real-time, both apps active | Immediate |
| `transferUserInfo` | Queued data | Guaranteed, in order |
| `transferFile` | Large data | Background transfer |

```swift
// Application Context (most common)
func updateContext(_ data: [String: Any]) throws {
    try WCSession.default.updateApplicationContext(data)
}

// Real-time messaging
func sendMessage(_ message: [String: Any]) {
    guard WCSession.default.isReachable else { return }
    WCSession.default.sendMessage(message, replyHandler: nil)
}

// Receiving data
func session(_ session: WCSession, didReceiveApplicationContext context: [String: Any]) {
    Task { @MainActor in
        // Update UI with received data
    }
}
```

## Complications

### Timeline Provider

```swift
import ClockKit

struct ComplicationController: CLKComplicationDataSource {

    func getComplicationDescriptors(handler: @escaping ([CLKComplicationDescriptor]) -> Void) {
        let descriptor = CLKComplicationDescriptor(
            identifier: "myComplication",
            displayName: "My App",
            supportedFamilies: [.circularSmall, .modularSmall, .graphicCircular]
        )
        handler([descriptor])
    }

    func getCurrentTimelineEntry(
        for complication: CLKComplication,
        withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void
    ) {
        let template = makeTemplate(for: complication.family)
        let entry = CLKComplicationTimelineEntry(date: .now, complicationTemplate: template)
        handler(entry)
    }
}
```

### WidgetKit Complications (watchOS 9+)

```swift
import WidgetKit
import SwiftUI

struct MyComplication: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(
            kind: "MyComplication",
            provider: ComplicationProvider()
        ) { entry in
            ComplicationView(entry: entry)
        }
        .configurationDisplayName("My Complication")
        .supportedFamilies([
            .accessoryCircular,
            .accessoryRectangular,
            .accessoryCorner,
            .accessoryInline
        ])
    }
}
```

## UI Components

### Digital Crown

```swift
@State private var crownValue = 0.0

ScrollView {
    // Content
}
.focusable()
.digitalCrownRotation($crownValue)
```

### Haptic Feedback

```swift
WKInterfaceDevice.current().play(.click)
WKInterfaceDevice.current().play(.success)
WKInterfaceDevice.current().play(.failure)
```

### Now Playing

```swift
import WatchKit

NowPlayingView()  // Built-in now playing controls
```

## Workout Apps

```swift
import HealthKit

@Observable
class WorkoutManager {
    let healthStore = HKHealthStore()
    var session: HKWorkoutSession?
    var builder: HKLiveWorkoutBuilder?

    func startWorkout(type: HKWorkoutActivityType) async throws {
        let config = HKWorkoutConfiguration()
        config.activityType = type
        config.locationType = .outdoor

        session = try HKWorkoutSession(healthStore: healthStore, configuration: config)
        builder = session?.associatedWorkoutBuilder()

        session?.startActivity(with: .now)
        try await builder?.beginCollection(at: .now)
    }
}
```

## Best Practices

### Performance
- Use `@Observable` over `ObservableObject` (watchOS 10+)
- Limit background refreshes
- Cache images locally
- Use lazy loading for lists

### Battery
- Minimize location updates
- Use scheduled background tasks
- Prefer complications over frequent refreshes
- Batch network requests

### User Experience
- Always show loading states
- Provide haptic feedback
- Support keyboard input
- Use clear iconography

## Testing

### Simulator
- Test with different watch sizes
- Verify complications in all families
- Test Watch Connectivity with paired iPhone simulator

### On Device
- Test battery impact
- Verify haptics feel appropriate
- Test in different lighting conditions

## References

- [watchOS Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/designing-for-watchos)
- [Watch Connectivity](https://developer.apple.com/documentation/watchconnectivity)
- [ClockKit](https://developer.apple.com/documentation/clockkit)
- [HealthKit Workouts](https://developer.apple.com/documentation/healthkit/workouts_and_activity_rings)

Overview

This skill provides practical guidance for building watchOS apps with SwiftUI, Watch Connectivity, complications, and watch-specific UI patterns. It focuses on watch-first design, performance, and battery-aware architecture to deliver fast, glanceable experiences. Use it for code reviews, best practices, or implementation help across common watchOS features.

How this skill works

The skill inspects design choices, architecture patterns, and code snippets for NavigationStack, List styles, Watch Connectivity session setup, complication providers, and workout/HealthKit integration. It explains appropriate data transfer methods (application context, sendMessage, transferUserInfo, transferFile), complication timelines, and WidgetKit-based complications. It highlights platform-specific APIs like digital crown handling, haptics, and Now Playing integration.

When to use it

  • Designing a watch-first UI that’s glanceable and fast
  • Implementing or reviewing Watch Connectivity sync and transfer strategies
  • Creating complications using ClockKit or WidgetKit providers
  • Optimizing battery, background work, and complication update cadence
  • Building workout or HealthKit-powered watch experiences

Best practices

  • Design for quick interactions (≤2 seconds) and show only essential info
  • Prefer independent watch apps and cache data locally for offline use
  • Use appropriate connectivity APIs: applicationContext for state, sendMessage for real-time, transferUserInfo for queued delivery
  • Limit background and network work; prefer timeline-based complication updates
  • Use large touch targets, clear icons, loading states, and haptic feedback

Example use cases

  • Code review of a SwiftUI watch app using NavigationStack and TabView
  • Implementing a complication with timeline entries or a WidgetKit accessory
  • Syncing settings and small data from iPhone using updateApplicationContext
  • Building a workout session with HealthKit and live metrics
  • Tuning battery usage by batching network requests and reducing location polls

FAQ

Should I make an independent watch app or a companion?

Prefer independent watch apps when possible so the watch is usable without the phone; use Watch Connectivity for nonessential sync and cache data locally for offline access.

Which Watch Connectivity method should I use for each case?

Use updateApplicationContext for current state/settings, sendMessage for real-time interactions when both apps are active, transferUserInfo for guaranteed queued transfers, and transferFile for large payloads.