Agent Skills: Widget Generator

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.

UncategorizedID: rshankras/claude-code-apple-skills/widget-generator

Install this agent skill to your local

pnpm dlx add-skill https://github.com/rshankras/claude-code-apple-skills/tree/HEAD/skills/generators/widget-generator

Skill Files

Browse the full folder contents for widget-generator.

Download Skill

Loading file tree…

skills/generators/widget-generator/SKILL.md

Skill Metadata

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.

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:

@main
struct MyAppWidgets: WidgetBundle {
    var body: some Widget {
        // Existing widgets...
        {Name}Widget()
    }
}

4. Trigger widget updates from the main app when data changes:

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:

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