rshankras/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.
npx skills add https://github.com/rshankras/claude-code-apple-skills --skill 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.
Use this skill when the user:
WidgetBundleSearch for existing widget code:
Glob: **/*Widget*.swift, **/*TimelineProvider*.swift
Grep: "WidgetKit" or "TimelineProvider" or "WidgetBundle" or "WidgetConfiguration"
If an existing widget extension is found:
WidgetBundle to extendIf a WidgetBundle already exists:
@main entry pointIf widget code with the same name exists:
Widgets require:
Ask user via AskUserQuestion:
StaticConfiguration) -- content updated on a schedule, no user configurationAppIntentConfiguration, iOS 17+) -- user can choose what the widget displays via long-press editAppIntentConfiguration + Button/Toggle, iOS 17+) -- user can tap buttons or toggles directly on the widgetWidgetCenter.shared.reloadTimelines(ofKind:)Check project structure:
For widget extension files:
MyAppWidgets/)For shared data models (if using App Groups):
Sources/ or Shared/ exists --> place thereGenerate these files based on configuration answers:
{Name}Widget.swift -- Widget definition with configurationWidget struct with StaticConfiguration or AppIntentConfiguration{Name}TimelineProvider.swift -- Timeline logicTimelineProvider (static) or AppIntentTimelineProvider (configurable){Name}Entry.swift -- Timeline entry modelTimelineEntry struct with date and display data{Name}WidgetViews.swift -- Size-specific viewscontainerBackground for iOS 17+ removable backgrounds{Name}AppIntent.swift (if interactive or configurable)WidgetConfigurationIntent for configurable widgetsAppIntent for interactive widget buttons/togglesUse the templates in templates.md and customize based on user answers:
Apply these when generating the size-specific views. A good widget idea is at least one of personal (the user's own data), informational (the most-wanted status at a glance), or contextual (the right thing for this moment). Glanceability is the bar — a widget is read in about a second on the way somewhere else.
| Rule | Specific |
|------|----------|
| Small widget | Max ~4 pieces of information; exactly one tap target (the whole widget) |
| Medium/Large | Multiple tap targets allowed — cell style for visually contained targets, content style for uncontained lists |
| Margins | 16pt default; 11pt acceptable for tighter graphical shapes (circles, glyphs) |
| Corners | Inner shapes must be concentric with the widget's corner radius |
| Layouts | Design each family separately — never scale the small layout up to medium/large |
| Typography | SF Pro, SF Pro Rounded, or SF Mono; custom fonts only when core to the brand |
| Logo | Only if the app aggregates content from multiple sources — and always top-right |
| Text bans | ❌ Instructional text ("tap to refresh") · ❌ "last updated/checked" language |
| Appearance | Support both light and dark |
| Placeholder | Ship a placeholder state (blocked shapes where content will be) for pre-data loading |
TimelineEntryRelevance) so yours surfaces at the right moment.After generation, provide:
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
1. Add the widget extension target (if not present):
2. Enable App Groups (if sharing data with the main app):
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")
#Preview with timeline entry data for rapid iteration.WidgetCenter.shared.getCurrentConfigurations to verify registered widgets..after(nextEventDate) policyButton(intent:) for each action (iOS 17+)Text(date, style: .timer) / Text(date, style: .relative) for automatic live updatescontainerBackground 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.AccessoryWidgetBackground() for backgrounds and keep designs simple with high contrast.Text(date, style: .timer) for countdowns; the system animates these for you.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.AppIntent parameter values are stored by the system. Do not rely on UserDefaults for configuration state.#Preview(as: .systemSmall) for family-specific widget previews.Take rshankras/widget-generator from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
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.