Mobile Development4 min read

WidgetKit

Apple's framework for building Home Screen, Lock Screen, and StandBy widgets - declarative SwiftUI views updated on a timeline, not real-time, with a separate extension process

iOSAppleHome ScreenLock ScreenSwiftUI
---
name: widgetkit
description: >
  You are a WidgetKit developer. You build Home Screen and Lock Screen widgets with TimelineProvider, App Groups for data sharing, widget families, and interactive buttons via App Intents on iOS 17+.
---

WidgetKit widgets are not live views - they are snapshots rendered by the system at times determined by a TimelineProvider. The widget extension is a separate process with no direct communication with the app; all data sharing goes through App Groups. Understanding this rendering model is the prerequisite for everything else in WidgetKit.

Workflow

  1. Set up an App Group between the app target and the widget extension before writing any codeThe widget extension runs in a separate process; UserDefaults(suiteName:) and FileManager with App Group container are the only ways to share data - forgetting the App Group is the most common cause of blank widgets
  2. Implement getSnapshot() to return a placeholder entry that looks good immediatelyThe system calls getSnapshot() when showing the widget in the widget gallery; a slow or empty snapshot makes users not add the widget - return hardcoded sample data
  3. Return a timeline with a .atEnd policy for data that changes on a schedule; use WidgetCenter.shared.reloadTimelines(ofKind:) from the app for event-driven updatesThe system throttles timeline reloads; requesting more frequent updates than needed drains battery and the system ignores the requests anyway - only reload when the app has new data
  4. Support all three widget families (.systemSmall, .systemMedium, .systemLarge) and lock screen families (.accessoryCircular, .accessoryRectangular) with distinct layoutsWidgets displayed in the wrong family crop or stretch incorrectly; always use @Environment(\.widgetFamily) to switch layout based on the family the system selected
  5. Add App Intents for interactive widget buttons on iOS 17+Before iOS 17, widgets were static; buttons trigger an AppIntent that runs in the extension process - not a deep link to the app - enabling actions like toggling a task complete without opening the app

Output Format

A production widget has a TimelineProvider that fetches from a shared App Group UserDefaults, a TimelineEntry with all data needed for the view, and a SwiftUI body that switches layout based on widgetFamily.

struct Provider: TimelineProvider {
  func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {
    completion(Entry(date: .now, data: .sample))
  }
  func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
    let entry = Entry(date: .now, data: SharedData.load())
    let refresh = Calendar.current.date(byAdding: .hour, value: 1, to: .now)!
    completion(Timeline(entries: [entry], policy: .after(refresh)))
  }
}

Examples

Sharing data with App Group

Input

Pass the user's daily step count from the main app to the widget

Output

App: UserDefaults(suiteName: "group.com.app")?.set(stepCount, forKey: "steps"); then WidgetCenter.shared.reloadAllTimelines(). Widget: let steps = UserDefaults(suiteName: "group.com.app")?.integer(forKey: "steps")

Wrong: accessing main app files from widget

Input

Reading a file from the main app's Documents directory

Output

The widget extension runs in a sandboxed process with no access to the main app's container; the file read silently returns nil and the widget shows blank

Right: App Group shared container

Input

Same file

Output

Write the file to FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.app") in both targets; both processes can read and write the same file

Edge Cases & Gotchas

  • Widget not updating after app data changes - WidgetCenter.shared.reloadTimelines(ofKind:) must be called from the main app process after data changes; calling it from the widget extension has no effect
  • Lock Screen widgets must use grayscale-aware colors - the Lock Screen renders in a reduced color environment; test with the Xcode widget preview set to .accessoryRectangular family to see how your colors appear
  • Interactive widget buttons require the action to complete in under 30 seconds - AppIntents that call a network API and exceed the timeout are silently cancelled by the system

When to Break These Rules

  • Use a Live Activity instead of a widget for time-sensitive, real-time information (sports scores, delivery tracking, ride status) - Live Activities update in real time via push notifications and appear on the Lock Screen and Dynamic Island
  • Use a Today Extension (deprecated) pattern via a Link widget if you truly need to open the app on tap - widgets cannot render arbitrary interactive UI before iOS 17

Reference Files

  • src/data/skills/index.js - add skill entries here
  • src/components/Skills/DocRenderer.js - renders this doc format

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.