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.
Attach @WidgetBundleBuilder to WidgetBundle.body
The
bodyrequirement of theWidgetBundleprotocol 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 theWeatherWidgetandCalendarWidgetnamed in the example, becomes one component the builder gathers into the bundle's singlesome Widgetresult.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
ViewBuilderstitches views in abody. This is what turns a flat list — the three widgets shown asWeatherWidget,CalendarWidget, andStepsWidget— into the multi-widget bundle the system installs, without any explicit container type.Stay within the supported component limit
WidgetBundleBuilderprovidesbuildBlockoverloads for assembling its widget components, so a bundle can list several widgets directly while still satisfyingbody's single-return shape. The trio of widgets illustrated here sits comfortably within that block; longer bundles nest additional widgets to keep eachbuildBlockwithin its supported arity.Return the composite Widget the system reads
Whatever the builder produces is the opaque
some WidgetyourWidgetBundle.bodyreturns, and the widget extension's@mainentry point reads it to register every contained widget with the gallery. That is why the example frames@WidgetBundleBuilderas the thing that "powersWidgetBundle.body" — the individual labels forWeatherWidget,CalendarWidget, andStepsWidgetstand in for the widgets the builder hands back as one value.
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.
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()
}
}