SwiftUI's declarative syntax makes it possible to describe complex interfaces by composing smaller pieces of content.
As a view hierarchy grows, however, the Swift compiler has historically had more work to do when determining exactly what kind of content each result-builder closure produces. In sufficiently complex hierarchies, that work can lead to a familiar compiler diagnostic:
The compiler is unable to type-check this expression in reasonable timeWith Xcode 27, SwiftUI introduces ContentBuilder, a unified builder designed to simplify this process.
ContentBuilder replaces many of SwiftUI's type-specific builder paths with a common, type-agnostic mechanism. The result is simpler type inference and substantially improved type-checking performance, particularly for deeply nested SwiftUI code.
In this article, we'll look at why SwiftUI needed ContentBuilder, how it works, how it relates to ViewBuilder, and when you should use it in your own APIs.
ContentBuilder is a custom parameter attribute for constructing SwiftUI content from closures containing one or more statements.
Its declaration is intentionally simple:
typealias ContentBuilder = ViewBuilderAt first glance, this might make ContentBuilder look like little more than a new name for ViewBuilder.
The important change is how SwiftUI and the compiler treat builder content when building with Xcode 27.
Historically, SwiftUI used different result builders depending on the kind of content being constructed. A closure might produce views, toolbar content, commands, table rows, or another specialized content type.
With Xcode 27, SwiftUI moves toward a unified model where these builders can assemble content without first constraining that content to a particular protocol.
Instead of asking:
"Which kind of builder am I dealing with?"
SwiftUI can first assemble the content and determine whether the resulting content satisfies the surrounding context.
That distinction becomes especially important in deeply nested hierarchies.
Consider a simplified SwiftUI hierarchy:
struct ContentView: View {
let categories: [Category]
var body: some View {
List {
Section("Library") {
Group {
ForEach(categories) { category in
VStack(alignment: .leading) {
Text(category.name)
ForEach(category.items) { item in
Text(item.title)
}
}
}
}
}
}
}
}There is nothing particularly unusual about this code.
It contains several common SwiftUI building blocks:
Section
└── Group
└── ForEach
└── VStack
└── ForEachBefore the unified builder model, however, the compiler couldn't immediately assume what kind of content each container was producing.
Take Section as an example.
A section isn't exclusively a view-building primitive. Depending on where it's used, SwiftUI can use sections to construct different kinds of content.
The compiler therefore needs to determine which initializer is appropriate.
Conceptually, the process can begin to resemble this:
Section
├── View content?
│ └── Group
│ ├── View content?
│ └── Other content?
│
└── Other content?
└── Group
├── View content?
└── Other content?Now place a ForEach inside the Group.
The compiler encounters another set of possibilities.
Add another Group, conditional branch, Section, or ForEach, and the number of combinations it potentially needs to evaluate continues to grow.
The compiler eventually reaches your actual views, but it may need to explore a large number of possible type-checking paths before getting there.
Suppose you write:
Section {
Group {
ForEach(items) { item in
// Content
}
}
}As the developer, you already know what this code is doing.
You're building views.
But historically, that information wasn't necessarily available to the compiler when it started resolving the expression.
It first needed to determine what Section was building.
To answer that, it needed to understand the Group.
To understand the Group, it needed to understand the ForEach.
And to resolve the ForEach, it finally needed to inspect its content.
The deeper the hierarchy became, the more expensive that search could become.
In extreme cases, Swift's type checker eventually stopped trying and produced:
The compiler is unable to type-check this expression in reasonable timeDevelopers commonly worked around this by breaking large expressions into smaller computed properties or separate views.
For example:
var body: some View {
List {
librarySection
}
}
private var librarySection: some View {
Section("Library") {
libraryContent
}
}
private var libraryContent: some View {
ForEach(categories) { category in
CategoryView(category: category)
}
}Breaking large views into smaller components is still good SwiftUI design when it improves readability and responsibility boundaries.
But ideally, you shouldn't have to restructure otherwise reasonable code merely to help the type checker.
That's one of the problems ContentBuilder addresses.
With Xcode 27, SwiftUI unifies many of these builder paths around ContentBuilder.
Instead of constraining the builder based on the type of content it is expected to produce, ContentBuilder can initially assemble type-agnostic content.
That changes the compiler's job.
Instead of exploring multiple builder-specific overloads such as:
Section
├── View builder
├── Table row builder
└── ...the compiler can work through a much more direct path:
Section
└── ContentBuilder
└── Group
└── ContentBuilder
└── ForEach
└── ContentBuilderThe surrounding context ultimately determines whether the resulting content satisfies the protocol it needs to satisfy.
This eliminates much of the overload search that previously made nested builder expressions expensive to type-check.
The syntax you write remains largely unchanged.
The compiler simply has less ambiguity to resolve.
An important distinction is that ContentBuilder being type-agnostic does not mean SwiftUI has abandoned type safety.
ContentBuilder doesn't enforce a particular content protocol while assembling the builder's expressions.
Instead, the content types it creates use conditional conformances.
For example, multiple expressions can be represented by a TupleContent. That content can conform to the protocol required by its context when the elements inside it satisfy the necessary requirements.
Conceptually:
Build the content
↓
Determine its resulting type
↓
Check whether that content satisfies the surrounding contextrather than:
Guess the required builder
↓
Type-check the entire closure
↓
Try another builder if necessary
↓
RepeatThe result preserves Swift's compile-time type safety while reducing the amount of work necessary to reach that result.
ContentBuilder isn't only an internal SwiftUI implementation detail.
You can use it when defining your own APIs that accept SwiftUI-style content closures.
Consider a reusable card:
struct Card<Content: View>: View {
private let content: Content
init(@ContentBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
content
}
.padding()
.background(.regularMaterial)
.clipShape(.rect(cornerRadius: 16))
}
}You can then use the component with familiar SwiftUI syntax:
Card {
Text("Recent Activity")
.font(.headline)
Text("You completed 4 tasks today.")
.foregroundStyle(.secondary)
Button("View Details") {
// Show details
}
}The closure contains multiple statements, and @ContentBuilder combines them into content that satisfies the View constraint required by Card.
Conditional content works naturally as well:
Card {
Text("Account")
.font(.headline)
if isPremium {
Label("Premium", systemImage: "star.fill")
}
Button("Manage Account") {
showAccountSettings = true
}
}From the API consumer's perspective, this feels exactly like the SwiftUI composition model developers already know.
This is where the new name becomes particularly meaningful.
ViewBuilder naturally suggests:
This closure builds views.
ContentBuilder represents the broader direction SwiftUI is taking.
The builder can participate in APIs whose resulting content isn't necessarily just View.
For example, Apple demonstrates using @ContentBuilder for toolbar content:
@ContentBuilder
var editingToolbarItems: some ToolbarContent {
ForEach(EditingOptions.toolbarItems, id: \.self) { editingOption in
ToolbarItem {
Button(editingOption.title) {
editingOption.action()
}
}
}
}The same builder model can therefore participate in constructing View content as well as other SwiftUI content protocols.
This is the central idea behind builder unification.
Rather than maintaining a separate builder path for every kind of SwiftUI content, SwiftUI can increasingly share the same underlying composition machinery.
ViewBuilder isn't disappearing.
In fact, ContentBuilder is currently declared as a type alias:
typealias ContentBuilder = ViewBuilderWhen building with Xcode 27 or later, ViewBuilder also benefits from the new type-agnostic content construction behavior.
That means existing code such as:
struct Container<Content: View>: View {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
content
}
}doesn't suddenly stop working.
For new APIs where you expect the unified builder behavior, however, Apple recommends using the ContentBuilder spelling.
You could therefore write:
struct Container<Content: View>: View {
let content: Content
init(@ContentBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
content
}
}The change communicates something useful about the API.
You're describing a closure that constructs SwiftUI content, rather than explicitly tying its conceptual role to views alone.
One of the most useful aspects of this change is that its type-checking improvements aren't limited to apps whose minimum deployment target is one of the 2027 operating systems.
ContentBuilder can be used with older deployment targets.
The important requirement for the new type-checking behavior is building the project with Xcode 27.
That means an application can build with Xcode 27 while continuing to support an earlier OS release and still benefit from the compiler improvements associated with unified content builders.
This makes ContentBuilder unusual compared with many WWDC API announcements.
You don't necessarily need to raise your minimum deployment target just to benefit from the improvement.
Suppose you have an existing reusable SwiftUI component:
struct SettingsSection<Content: View>: View {
let title: String
let content: Content
init(_ title: String, @ViewBuilder content: () -> Content) {
self.title = title
self.content = content()
}
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text(title)
.font(.headline)
content
}
}
}When adopting Xcode 27, you can express the builder using ContentBuilder:
struct SettingsSection<Content: View>: View {
let title: String
let content: Content
init(_ title: String, @ContentBuilder content: () -> Content) {
self.title = title
self.content = content()
}
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text(title)
.font(.headline)
content
}
}
}Call sites remain unchanged:
SettingsSection("Notifications") {
Toggle("Push Notifications", isOn: $pushEnabled)
Toggle("Email Notifications", isOn: $emailEnabled)
if pushEnabled {
Picker("Frequency", selection: $frequency) {
Text("Immediately").tag(Frequency.immediately)
Text("Daily").tag(Frequency.daily)
}
}
}This is an important characteristic of the change.
For most SwiftUI applications, adopting the new builder model doesn't require redesigning the view hierarchy or changing how developers write SwiftUI at call sites.
The improvement primarily changes what happens while the compiler is trying to understand that code.
There is one implementation detail worth knowing when migrating more advanced SwiftUI code.
Unified ContentBuilder can produce TupleContent for multi-expression builder blocks where older code may have relied on concrete TupleView types.
Most applications never explicitly reference those concrete types.
For example, code like this is unaffected conceptually:
var body: some View {
VStack {
Text("Title")
Text("Subtitle")
}
}Problems are more likely when a custom generic API explicitly constrains itself to concrete builder implementation types.
For example, if your library contains constraints involving:
TupleView<(Text, Text)>you may encounter source compatibility issues when moving to the unified builder implementation.
Apple's guidance is to prefer opaque result types such as:
some Viewinstead of depending on concrete builder-generated types whenever possible.
This keeps your code aligned with SwiftUI's abstraction model and reduces its dependency on implementation details that may evolve.
Probably not as a mechanical migration.
Existing @ViewBuilder code continues to work when building with Xcode 27, and it benefits from the underlying improvements.
Instead, think of @ContentBuilder as the preferred expression for APIs designed around SwiftUI's newer unified content-building model.
For new reusable APIs, you might prefer:
init(@ContentBuilder content: () -> Content)rather than:
init(@ViewBuilder content: () -> Content)especially when "content" more accurately describes the abstraction you're building.
There's little value in performing a large search and replace solely for syntax.
The bigger benefit comes from building your SwiftUI project with Xcode 27.
ContentBuilder is easy to overlook because it doesn't introduce a new visual component.
There isn't a new control to place on screen.
There isn't a modifier that changes how your interface looks.
And most existing SwiftUI code doesn't need to change.
Instead, ContentBuilder improves one of the less visible parts of SwiftUI development: how the compiler understands declarative UI code.
By unifying previously distinct builder paths, SwiftUI can avoid exploring many equivalent overload possibilities when type-checking nested content.
For developers, that can mean:
It also points toward a broader evolution of SwiftUI.
Views, toolbar items, commands, rows, and other declarative content may look different at the API level, but they share a fundamental operation:
They assemble structured content from declarative closures.
ContentBuilder gives SwiftUI a common foundation for doing that.
Apple Developer Documentation: ContentBuilder
https://developer.apple.com/documentation/swiftui/contentbuilder
Apple Developer Documentation: ViewBuilder
https://developer.apple.com/documentation/swiftui/viewbuilder
Apple Technical Note TN3211: Resolving SwiftUI source incompatibilities for State and ContentBuilder
WWDC26: What's new in SwiftUI
https://developer.apple.com/videos/play/wwdc2026/269?time=1463
SwiftUI's result builders are one of the key pieces of language infrastructure that make declarative UI possible.
Instead of requiring the compiler to determine which specialized builder path it should follow before understanding a closure's content, SwiftUI can assemble that content first and validate its capabilities through the surrounding context.
The API itself is small:
@ContentBuilderbut the architectural change behind it is much larger.
If you're moving your SwiftUI projects to Xcode 27, you can benefit from the improved builder implementation even when supporting earlier OS versions. And when designing new SwiftUI APIs of your own, ContentBuilder provides a more general way to describe declarative SwiftUI content.
Thank you for reading. If you have any questions feel free to follow me on X and send me a DM. If this article helped you, Buy me a coffee.