home / skills / rshankras / claude-code-apple-skills / widget-generator

widget-generator skill

/skills/generators/widget-generator

This is most likely a fork of the generators skill from rshankras
npx playbooks add skill rshankras/claude-code-apple-skills --skill widget-generator

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

Files (2)
SKILL.md
12.0 KB
---
name: widget-generator
description: Generate WidgetKit widgets for iOS/macOS home screen and lock screen with timeline providers, interactive elements, and App Intent configuration. Use when adding widgets to an app.
allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion]
---

# Widget Generator

Generate a complete WidgetKit widget implementation with timeline providers, size-specific views, lock screen accessory widgets, interactive elements (iOS 17+), and App Intent configuration.

## When This Skill Activates

Use this skill when the user:
- Asks to "add widgets" or "add a widget" to their app
- Mentions "WidgetKit" or "home screen widgets"
- Wants "lock screen widgets" or "accessory widgets"
- Asks about "widget timelines" or "timeline providers"
- Wants "interactive widgets" with buttons or toggles
- Mentions "widget configuration" or "configurable widgets"
- Asks about "App Intent widgets" or "AppIntentConfiguration"
- Wants to show data on the home screen or lock screen

## Pre-Generation Checks

### 1. Project Context Detection
- [ ] Check for existing widget extension target
- [ ] Check for an existing `WidgetBundle`
- [ ] Verify deployment target (iOS 17+ recommended for interactive widgets)
- [ ] Identify source file locations and project structure

### 2. Conflict Detection

Search for existing widget code:
```
Glob: **/*Widget*.swift, **/*TimelineProvider*.swift
Grep: "WidgetKit" or "TimelineProvider" or "WidgetBundle" or "WidgetConfiguration"
```

If an existing widget extension is found:
- Ask if the new widget should be added to the existing widget extension
- Check for an existing `WidgetBundle` to extend

If a `WidgetBundle` already exists:
- Add the new widget to the existing bundle instead of creating a new one
- Do NOT create a second `@main` entry point

If widget code with the same name exists:
- Ask user whether to replace or rename

### 3. Required Capabilities

**Widgets require:**
- A widget extension target (File > New > Target > Widget Extension)
- App Groups capability if sharing data between the main app and widget
- iOS 14+ for basic widgets, iOS 16+ for lock screen, iOS 17+ for interactive widgets

## Configuration Questions

Ask user via AskUserQuestion:

1. **What is this widget for?** (freeform)
   - Examples: weather forecast, task list, fitness stats, quick actions, countdown timer
   - This determines the data model and timeline update strategy

2. **Which widget sizes should be supported?**
   - Home screen: systemSmall, systemMedium, systemLarge, systemExtraLarge (iPad only)
   - Lock screen: accessoryCircular, accessoryRectangular, accessoryInline
   - All home screen sizes
   - All home screen + lock screen sizes (recommended)

3. **What type of widget?**
   - **Static** (`StaticConfiguration`) -- content updated on a schedule, no user configuration
   - **Configurable** (`AppIntentConfiguration`, iOS 17+) -- user can choose what the widget displays via long-press edit
   - **Interactive** (`AppIntentConfiguration` + `Button`/`Toggle`, iOS 17+) -- user can tap buttons or toggles directly on the widget

4. **What is the data source?**
   - Local data (UserDefaults, SwiftData, Core Data)
   - Shared data via App Groups (main app writes, widget reads)
   - Network API (fetched during timeline refresh)
   - Combination of local and network

5. **How often should the widget update?**
   - Every 15 minutes (minimum practical interval)
   - Every 30 minutes
   - Every hour (recommended default)
   - A few times per day
   - Based on specific times (e.g., calendar events)
   - On-demand from the main app via `WidgetCenter.shared.reloadTimelines(ofKind:)`

## Generation Process

### Step 1: Determine File Locations

Check project structure:
- If a widget extension target directory exists, add view and provider files there
- Otherwise, instruct user to create a Widget Extension target first

For widget extension files:
- Place inside the existing widget extension directory (e.g., `MyAppWidgets/`)

For shared data models (if using App Groups):
- If `Sources/` or `Shared/` exists --> place there
- Otherwise --> create alongside existing models

### Step 2: Create Core Files

Generate these files based on configuration answers:

1. **`{Name}Widget.swift`** -- Widget definition with configuration
   - `Widget` struct with `StaticConfiguration` or `AppIntentConfiguration`
   - Supported families declaration
   - Display name and description
2. **`{Name}TimelineProvider.swift`** -- Timeline logic
   - `TimelineProvider` (static) or `AppIntentTimelineProvider` (configurable)
   - Placeholder, snapshot, and timeline methods
3. **`{Name}Entry.swift`** -- Timeline entry model
   - `TimelineEntry` struct with date and display data
4. **`{Name}WidgetViews.swift`** -- Size-specific views
   - Separate view struct for each supported family
   - Uses `containerBackground` for iOS 17+ removable backgrounds
5. **`{Name}AppIntent.swift`** (if interactive or configurable)
   - `WidgetConfigurationIntent` for configurable widgets
   - `AppIntent` for interactive widget buttons/toggles
6. **WidgetBundle update** -- Register the new widget
   - Add to existing bundle or create new one

### Step 3: Generate Code from Templates

Use the templates in **templates.md** and customize based on user answers:
- Replace placeholder names with the actual widget name
- Configure supported families based on size selection
- Include or exclude interactive elements
- Include or exclude lock screen accessory views
- Set up App Group shared data access if needed
- Configure timeline refresh policy based on update frequency

## Output Format

After generation, provide:

### Files Created

```
MyAppWidgets/
├── {Name}Widget.swift              # Widget definition + configuration
├── {Name}TimelineProvider.swift     # Timeline provider with placeholder/snapshot/timeline
├── {Name}Entry.swift               # TimelineEntry data model
├── {Name}WidgetViews.swift         # Size-specific views for each family
├── {Name}AppIntent.swift           # (if configurable/interactive) App Intent
└── (update WidgetBundle if needed)

Shared/
└── {Name}DataProvider.swift        # (if App Groups) Shared data access
```

### Integration Steps

**1. Add the widget extension target (if not present):**
- File > New > Target > Widget Extension
- Choose "Include Configuration App Intent" if configurable
- Ensure the widget extension embeds in the main app

**2. Enable App Groups (if sharing data with the main app):**
- Select the main app target > Signing & Capabilities > + Capability > App Groups
- Select the widget extension target > Signing & Capabilities > + Capability > App Groups
- Use the same group identifier (e.g., `group.com.yourcompany.yourapp`)

**3. Register the widget in the WidgetBundle:**
```swift
@main
struct MyAppWidgets: WidgetBundle {
    var body: some Widget {
        // Existing widgets...
        {Name}Widget()
    }
}
```

**4. Trigger widget updates from the main app when data changes:**
```swift
import WidgetKit

// Reload a specific widget
WidgetCenter.shared.reloadTimelines(ofKind: "{Name}Widget")

// Or reload all widgets
WidgetCenter.shared.reloadAllTimelines()
```

**5. For App Group data sharing, write from the main app:**
```swift
let sharedDefaults = UserDefaults(suiteName: "group.com.yourcompany.yourapp")
sharedDefaults?.set(encodedData, forKey: "widgetData")

// Then trigger reload
WidgetCenter.shared.reloadTimelines(ofKind: "{Name}Widget")
```

### Testing Instructions

1. **Simulator support:** Widgets can be previewed in Xcode Canvas and tested in Simulator.
2. **Add to home screen:** Long-press the home screen > tap "+" > find your app > select the widget size.
3. **Lock screen widgets:** Long-press the lock screen > "Customize" > select the widget area.
4. **Preview in Xcode:** Use `#Preview` with timeline entry data for rapid iteration.
5. **Timeline debugging:** Use `WidgetCenter.shared.getCurrentConfigurations` to verify registered widgets.
6. **Interactive widget testing (iOS 17+):** Tap buttons/toggles directly on the widget in Simulator or device.
7. **Memory profiling:** Widgets have a 40MB memory limit. Profile in Instruments if loading images or large datasets.

## Common Widget Patterns

### Weather Widget
- **Data:** Temperature, condition icon, hourly forecast
- **Sizes:** systemSmall (current temp), systemMedium (hourly), accessoryCircular (temp gauge)
- **Update:** Every 30 minutes via network API
- **Timeline:** Generate entries for next few hours with forecast data

### Calendar / Events Widget
- **Data:** Upcoming events, times, locations
- **Sizes:** systemSmall (next event), systemMedium (next 3 events), accessoryRectangular (next event)
- **Update:** Based on event start times using `.after(nextEventDate)` policy
- **Timeline:** One entry per upcoming event transition

### Fitness / Health Widget
- **Data:** Steps, calories, activity rings
- **Sizes:** systemSmall (ring summary), accessoryCircular (ring gauge), accessoryRectangular (stats)
- **Update:** Every 15-30 minutes from HealthKit via App Groups
- **Interactive:** None (read-only data display)

### Quick Actions Widget
- **Data:** Action buttons (start timer, toggle light, log water)
- **Sizes:** systemSmall (single action), systemMedium (2-4 actions)
- **Update:** Infrequent (actions are static, only state changes)
- **Interactive:** `Button(intent:)` for each action (iOS 17+)

### Countdown Widget
- **Data:** Target date, label, time remaining
- **Sizes:** systemSmall (days remaining), accessoryCircular (days number), accessoryInline (text countdown)
- **Update:** Daily or use `Text(date, style: .timer)` / `Text(date, style: .relative)` for automatic live updates
- **Timeline:** SwiftUI date styles update automatically without timeline refreshes

## Gotchas and Limits

- **Timeline budget:** The system limits how often your timeline provider runs. Typically ~40-70 refreshes per day. Do not rely on exact timing.
- **40MB memory limit:** Widget extensions are killed if they exceed 40MB. Avoid loading large images or datasets. Use thumbnails and minimal data.
- **`containerBackground` required (iOS 17+):** All widget views must use `.containerBackground(for: .widget)` to support the system's removable background feature. Without this, widgets show a default placeholder background.
- **Accessory family rendering:** Lock screen widgets render in a limited color space. Use `AccessoryWidgetBackground()` for backgrounds and keep designs simple with high contrast.
- **No animation:** Widgets do not support explicit animations. Use `Text(date, style: .timer)` for countdowns; the system animates these for you.
- **No scrolling:** Widgets cannot scroll. Design for fixed, visible content.
- **No video or maps:** MapKit and AVKit are not available in widget extensions.
- **Networking in timeline provider:** Network requests in `getTimeline` must complete quickly. The system may terminate long-running providers.
- **`@main` conflict:** Only one `@main` per widget extension. If you have multiple widgets, use a `WidgetBundle` as the single `@main` entry point.
- **Configurable widget data persistence:** `AppIntent` parameter values are stored by the system. Do not rely on UserDefaults for configuration state.
- **Xcode previews:** Use `#Preview(as: .systemSmall)` for family-specific widget previews.
- **Shared code with main app:** Timeline entries and data models referenced by both targets must have target membership in both the main app and the widget extension.

## References

- **templates.md** -- Production-ready code templates for widget definition, timeline provider, views, and App Intents
- [WidgetKit Documentation](https://developer.apple.com/documentation/widgetkit)
- [Creating a Widget Extension](https://developer.apple.com/documentation/widgetkit/creating-a-widget-extension)
- [Making a Configurable Widget](https://developer.apple.com/documentation/widgetkit/making-a-configurable-widget)
- [Adding Interactivity to Widgets](https://developer.apple.com/documentation/widgetkit/adding-interactivity-to-widgets-and-live-activities)
- [WidgetKit Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/widgets)