TechnologiesSwiftUI

WidgetBundleBuilder struct

iOSmacOStvOSwatchOSvisionOS✓ renders

A custom attribute that constructs a widget bundle's body.

How it works

WidgetBundleBuilder is the result builder that backs the body property of a WidgetBundle, the type an app uses to vend more than one kind of widget from a single extension. Because body requires some Widget, a bundle that listed several widgets in sequence would otherwise need to combine them by hand; @WidgetBundleBuilder lets you instead write each widget on its own line and have the compiler assemble them into one composite Widget. Reach for it implicitly whenever you declare a WidgetBundle whose body should expose multiple widgets to the user's widget gallery, such as WeatherWidget, CalendarWidget, and StepsWidget.

  1. Attach @WidgetBundleBuilder to WidgetBundle.body

    The body requirement of the WidgetBundle protocol is already annotated with @WidgetBundleBuilder, so you never apply the attribute yourself — you simply provide the property and the builder transforms its statements. Each widget you list, like the WeatherWidget and CalendarWidget named in the example, becomes one component the builder gathers into the bundle's single some Widget result.

  2. List widgets as statements rather than combining them

    Inside the builder closure you write one widget per line and the builder stitches them together, the same way ViewBuilder stitches views in a body. This is what turns a flat list — the three widgets shown as WeatherWidget, CalendarWidget, and StepsWidget — into the multi-widget bundle the system installs, without any explicit container type.

  3. Stay within the supported component limit

    WidgetBundleBuilder provides buildBlock overloads for assembling its widget components, so a bundle can list several widgets directly while still satisfying body's single-return shape. The trio of widgets illustrated here sits comfortably within that block; longer bundles nest additional widgets to keep each buildBlock within its supported arity.

  4. Return the composite Widget the system reads

    Whatever the builder produces is the opaque some Widget your WidgetBundle.body returns, and the widget extension's @main entry point reads it to register every contained widget with the gallery. That is why the example frames @WidgetBundleBuilder as the thing that "powers WidgetBundle.body" — the individual labels for WeatherWidget, CalendarWidget, and StepsWidget stand in for the widgets the builder hands back as one value.

Try it — Add a fourth Label("RemindersWidget", systemImage: "checklist") line alongside the existing three to see how @WidgetBundleBuilder absorbs an additional widget component into the same composite result without changing the shape of 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.

WidgetBundleBuilder.swift
struct WidgetBundleBuilderDemo: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("WidgetBundleBuilder")
                .font(.headline)
            Text("@WidgetBundleBuilder powers WidgetBundle.body, letting an app expose several widgets:")
                .font(.subheadline)
                .foregroundStyle(.secondary)
            VStack(alignment: .leading, spacing: 4) {
                Label("WeatherWidget", systemImage: "cloud.sun.fill")
                Label("CalendarWidget", systemImage: "calendar")
                Label("StepsWidget", systemImage: "figure.walk")
            }
            .font(.callout)
            .padding(10)
            .background(.quaternary, in: RoundedRectangle(cornerRadius: 10))
        }
        .padding()
    }
}
Live preview
WidgetBundleBuilder @WidgetBundleBuilder powers WidgetBundle.body, letting an app expose several widgets: WeatherWidget CalendarWidget StepsWidget
WidgetBundleBuilder @WidgetBundleBuilder powers WidgetBundle.body, letting an app expose several widgets: WeatherWidget CalendarWidget StepsWidget
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →