Result Builder is a Swift attribute implemented via the @resultBuilder protocol that transforms a sequence of expressions into a composite value. The compiler converts code blocks with control flow constructs if, for, switch into calls to static builder methods — buildBlock, buildEither, buildArray. According to the Swift Evolution proposal SE-0289 (2022), result builders allow creating declarative DSLs inside Swift without external parsers. The most famous example is @ViewBuilder in SwiftUI, where the view body is constructed from conditional and iterative elements in a declarative style.
Key Takeaways
TupleViewif/else, switch, for-in via buildOptional, buildEither, buildArray methodsResult Builder (formerly known as function builders) is a Swift mechanism that allows turning a sequence of expressions separated by line breaks into a single value. It is declared using the @resultBuilder attribute applied to a structure that implements static transformation methods.
Before result builders, the declarative syntax of SwiftUI body was impossible. Instead of a compact list of views, developers would have to write TupleView calls manually. Result Builder automatically wraps each expression, supports branching and loops, hiding the complexity of composition from the developer.
According to Swift Evolution SE-0289, accepted in 2022, the result builder is an evolution of the function builders idea (SE-0258, Swift 5.1). Key changes: renaming from @_functionBuilder to @resultBuilder and extension to function parameters, allowing builders to be used for any closure arguments, not just view bodies.
Use result builders when you need to provide users of your library with a declarative syntax for building complex structures — configurations, queries, UI components — without writing imperative assembly code.
The Swift compiler transforms each code block marked with @resultBuilder into a sequence of calls to static builder methods. Let’s look at a simple builder that concatenates strings:
@resultBuilder
struct StringBuilder {
static func buildBlock(_ parts: String...) -> String {
parts.joined(separator: " ")
}
}
Using this builder — each string on a separate line is concatenated with a space:
@StringBuilder
func greeting() -> String {
"Hello"
"World"
"from"
"Swift"
}
// Compiler transforms this into:
// StringBuilder.buildBlock("Hello", "World", "from", "Swift")
// Result: "Hello World from Swift"
The compiler groups consecutive expressions and passes them as variadic parameters to buildBlock. If an if appears between expressions, the compiler calls buildOptional or buildEither for branching. For for-in loops it calls buildArray. Thus, regular Swift code is transformed into a chain of calls that construct the final value.
Each result builder defines a set of static methods that the compiler calls during transformation. The main methods:
| Method | Purpose | When Called |
|---|---|---|
| buildBlock | Combines a sequence of expressions | For each block without branching |
| buildOptional | Handles if without else | When if appears without else |
| buildEither(first:) | First branch of if-else | For if with else |
| buildEither(second:) | Second branch of if-else | For if with else |
| buildArray | Handles for-in loops | When for-in is present |
| buildExpression | Transforms individual expressions | For each expression before passing to buildBlock |
| buildFinalResult | Final transformation | Before returning from the closure |
A minimal implementation requires only buildBlock with variadic parameters — this is sufficient for blocks without branching. Adding buildOptional and buildEither enables support for conditional constructs, and buildArray enables loops. According to Swift Documentation (2025), it is recommended to implement all methods for maximum DSL flexibility.
buildExpression allows accepting expressions of different types and converting them to a single builder type. For example, in @ViewBuilder, buildExpression accepts Text, Image, Button and converts them to the common View type.
Let’s look at creating a builder for constructing HTML strings. This DSL will allow writing declarative HTML directly in Swift:
@resultBuilder
enum HTMLBuilder {
static func buildBlock(_ components: String...) -> String {
components.joined()
}
static func buildOptional(_ component: String?) -> String {
component ?? ""
}
static func buildEither(first component: String) -> String {
component
}
static func buildEither(second component: String) -> String {
component
}
static func buildArray(_ components: [String]) -> String {
components.joined()
}
}
Using the custom builder to generate HTML:
func div(@HTMLBuilder _ content: () -> String) -> String {
"<div>\(content())</div>"
}
func p(_ text: String) -> String {
"<p>\(text)</p>"
}
let page = div {
p("Hello")
p("World")
if showFooter {
p("Footer")
}
}
// Result: <div><p>Hello</p><p>World</p><p>Footer</p></div>
According to the article “Building Custom Result Builders in Swift” from Swift.org (2025), custom builders are used in libraries for building configuration files, UI components, data mapping, and even database queries — anywhere where a declarative syntax with branching support is needed.
@ViewBuilder is a result builder built into SwiftUI that is applied to the content parameter of most container views: VStack, HStack, ZStack, Group, List, and the body property itself. It allows writing multiple views on separate lines without commas or wrappers.
@ViewBuilder implements all result builder methods, including support for if-else, switch, and for-in. When a condition is true, buildEither(first:) returns one view; when false, buildEither(second:) returns another. Both branches must return the same type, but SwiftUI uses AnyView internally or type erasure through ConditionalContent.
struct GreetingView: View {
let isLoggedIn: Bool
var body: some View {
VStack {
Image(systemName: "person.circle")
Text("Profile")
.font(.title)
if isLoggedIn {
Text("Welcome back!")
.foregroundColor(.green)
} else {
Button("Log In") { }
}
}
}
}
Without @ViewBuilder, the same code would require Group for each conditional section or using AnyView, which hurts performance. @ViewBuilder automatically selects the most efficient representation — ConditionalContent or TupleView — for each combination.
The first limitation is the maximum number of expressions in buildBlock. The Swift standard library defines buildBlock overloads for 2–10 expressions. If a block has more than 10 expressions, the compiler will produce an error. The solution is grouping via Group or VStack to split into sub-blocks.
The second limitation is the lack of support for variables and assignments inside the builder block. You cannot declare let x = 5 inside @ViewBuilder. All expressions must be expressions returning a value of the builder type. For intermediate calculations, use computations outside the builder or buildExpression with support for different types.
The third limitation is debugging complexity. Compilation errors inside a result builder often produce confusing messages, especially when types mismatch in if/else branches. Use explicit return types and AnyView for debugging, though the latter reduces performance. According to Hacking with Swift (2025), a practical tip is to start with a simple builder without branching and gradually add conditional construct support.
Frequently Asked Questions
Result Builder is a Swift attribute that transforms a sequence of expressions into a resulting value through static methods. It allows creating declarative DSLs, the most famous example being @ViewBuilder in SwiftUI for building view hierarchies without imperative code.
Declare a structure with the @resultBuilder attribute and implement at least the buildBlock method. To support conditions, add buildOptional and buildEither; for loops, add buildArray. Use the builder attribute before the closure parameter in a function.
Only buildBlock is mandatory. All other methods — buildOptional, buildEither, buildArray, buildExpression, buildFinalResult — are optional and add support for corresponding constructs. The more methods implemented, the more flexible the DSL.
@ViewBuilder is a concrete implementation of the result builder for the View protocol. It is defined in SwiftUI as a structure with the @resultBuilder attribute, providing buildBlock methods for different numbers of views (TupleView), buildEither for ConditionalContent, and buildArray for ForEach.
Yes, the standard buildBlock overloads support up to 10 expressions. When exceeding this, use nested containers (Group, VStack) to split into sub-blocks. A custom builder can define a variadic buildBlock without limitation.
Summary
buildBlock, buildEither, buildOptional, buildArray depending on control flow constructsWe will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also