TechnologiesSwiftUI

WidgetBundle protocol

iOSmacOStvOSwatchOSvisionOS✓ renders

A container used to expose multiple widgets from a single widget extension.

How it works

WidgetBundle is the protocol you adopt to ship more than one kind of widget from a single widget extension. A widget extension has exactly one entry point, so when your app offers several distinct widgets — a calendar, a weather glance, a reminders list — you group them under one WidgetBundle rather than declaring each in isolation. Reach for it whenever a target needs to vend multiple widgets, including Live Activities, so the system can present them all in the widget gallery for the user to add.

  1. Adopt WidgetBundle and mark the entry point

    Conform a type to WidgetBundle and annotate it with @main to make it the widget extension's single entry point, just as @main marks an app's App type. The conforming type carries no stored state of its own; its whole job is to enumerate the widgets the extension provides, the way WidgetBundleDemo here enumerates the names Calendar, Weather, and Reminders.

  2. List the widgets in the body property

    Implement the required body property, whose return type conforms to the Widget protocol. Inside it you list each widget value — one per kind you want to publish — and the system registers every one. Each entry corresponds to a configured widget, much as each name in the ForEach over widgets stands for one item in the bundle.

  3. Compose with @WidgetBundleBuilder

    The body property is a @WidgetBundleBuilder result builder, so you write the widgets as a plain sequence of expressions rather than assembling an array by hand. The builder also lets you branch with if and if #available to conditionally include a widget — for example, gating a Live Activity on the OS version — so a single bundle can adapt its offerings to the running platform.

  4. Combine different widget configurations

    A bundle can mix StaticConfiguration, AppIntentConfiguration, and Live Activity widgets freely; each retains its own kind, supported families, and configuration UI. Grouping them under one WidgetBundle only collects them into a single extension — it does not flatten or merge their behavior, so the three conceptual entries shown as Calendar, Weather, and Reminders would each keep distinct timelines and supported sizes.

Try it — Add a fourth entry such as "Stocks" to the widgets array to see how a WidgetBundle grows simply by listing one more widget in its body.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

WidgetBundle.swift
struct WidgetBundleDemo: View {
    let widgets = ["Calendar", "Weather", "Reminders"]
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("My Widget Bundle")
                .font(.headline)
            ForEach(widgets, id: \.self) { name in
                HStack {
                    Image(systemName: "square.grid.2x2")
                    Text(name)
                    Spacer()
                }
                .padding()
                .background(Color(.systemGray6))
                .cornerRadius(10)
            }
        }
        .padding()
    }
}
Live preview
My Widget Bundle Calendar Weather Reminders
My Widget Bundle Calendar Weather Reminders
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →