mcpField
W
widgetkit

widgetkit

github.com/ios-agentDevelopment1265 installsUpdated 35w ago
Repository

About widgetkit

A development skill for building WidgetKit experiences across Apple platforms. It covers widget layouts, rendering modes, interaction, configuration, and platform constraints.

Key features

  1. 01Widget structure and timelines
  2. 02Adaptation to widget families
  3. 03Rendering mode handling
  4. 04Buttons, toggles, and deep links
  5. 05Configuration types and constraints

Use cases

  1. 01Create Home Screen or Lock Screen widgets
  2. 02Design layouts for different widget sizes
  3. 03Add widget actions and app links

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/ios-agent/iosagent.dev --skill widgetkit

After installing, type/widgetkitin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
design-guidelines.md
dimensions.md
interactivity.md
live-activities.md
platforms.md
timelines.md

Details

From the project README

WidgetKit Development Skill

Build glanceable, timely experiences across Apple platforms using WidgetKit.

Quick Start

Create Widget Extension
  1. File → New → Target → Widget Extension
  2. Deselect "Include Live Activity" and "Include Configuration App Intent" for static widgets
  3. Widget requires: Widget protocol, TimelineProvider, and SwiftUI views
Minimal Widget Structure
@main
struct MyWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(
            kind: "com.app.mywidget",
            provider: Provider()
        ) { entry in
            MyWidgetView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("Shows key information")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: .now)
    }
    
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        completion(SimpleEntry(date: .now))
    }
    
    func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
        let entry = SimpleEntry(date: .now)
        let nextUpdate = Calendar.current.date(byAdding: .minute, value: 15, to: .now)!
        completion(Timeline(entries: [entry], policy: .after(nextUpdate)))
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
}

Widget Families & Sizes

FamilyPlatformsUse Case
systemSmalliOS, iPadOS, macOS, visionOSSingle tap target, glanceable info
systemMediumiOS, iPadOS, macOS, visionOSMultiple data points, interactive elements
systemLargeiOS, iPadOS, macOS, visionOSRich content, multiple interactions
systemExtraLargeiPadOS, macOS, visionOSDashboard-style layouts
accessoryCirculariOS Lock Screen, watchOSMinimal info, gauge-style
accessoryRectangulariOS Lock Screen, watchOS2-3 lines of text
accessoryInlineiOS Lock Screen, watchOSSingle line text + optional image
accessoryCornerwatchOS onlyCorner complications
Adapt to Widget Family
struct MyWidgetView: View {
    @Environment(\.widgetFamily) var family
    
    var body: some View {
        switch family {
        case .systemSmall: CompactView()
        case .systemMedium: MediumView()
        case .systemLarge: DetailedView()
        case .accessoryCircular: GaugeView()
        case .accessoryRectangular: RectangularView()
        default: CompactView()
        }
    }
}

Rendering Modes

Widgets render differently based on context:

ModeWhen UsedBehavior
fullColorHome Screen (iOS 17-), macOS desktopFull color preserved
accentedHome Screen tinted/clear, visionOS, watchOSDivides into accent + primary groups
vibrantLock Screen, StandByDesaturated, blurred effect
@Environment(\.widgetRenderingMode) var renderingMode

var body: some View {
    switch renderingMode {
    case .fullColor: FullColorView()
    case .accented: AccentedView()
    case .vibrant: VibrantView()
    @unknown default: FullColorView()
    }
}

Interactivity

Buttons & Toggles (iOS 17+)
Button(intent: RefreshIntent()) {
    Label("Refresh", systemImage: "arrow.clockwise")
}

Toggle(isOn: $isEnabled, intent: ToggleIntent()) {
    Text("Enable")
}
Deep Links
MyWidgetView()
    .widgetURL(URL(string: "myapp://detail/123")!)

// Or for multiple links in larger widgets:
Link(destination: URL(string: "myapp://item/1")!) {
    ItemView()
}

Configuration Types

TypeUse Case
StaticConfigurationNo user configuration needed
AppIntentConfigurationUser-configurable (iOS 17+)
ActivityConfigurationLive Activities

Reference Documentation

Key Constraints

  • No real-time updates: Use timelines; system batches updates
  • Limited budget: ~40-70 refreshes/day depending on usage
  • No continuous animations: Only transition animations up to 2 seconds
  • SwiftUI only: UIKit views not supported
  • Stateless: No persistent state between renders
  • No network in views: Fetch data in timeline provider only

Common Patterns

Share Data with Main App
// Use App Groups
let sharedDefaults = UserDefaults(suiteName: "group.com.app.shared")

// Or shared container
let containerURL = FileManager.default.containerURL(
    forSecurityApplicationGroupIdentifier: "group.com.app.shared"
)
Force Widget Refresh
import WidgetKit
WidgetCenter.shared.reloadTimelines(ofKind: "com.app.mywidget")
WidgetCenter.shared.reloadAllTimelines()
Placeholder for Sensitive Content
.privacySensitive() // Redacts when device locked

Comments

Markdown supported