Result Builders: Declarative Swift DSLs
Result builders teach the compiler to fold expressions into one combined value, enabling declarative DSLs. SwiftUI relies on them to compose stacked views. Type errors surface on compiler-generated boilerplate instead of your original code.
WHY IT EXISTS: Before result builders, creating a declarative API in Swift meant nesting function calls or using array literals, which produced pyramid-shaped code that obscured the underlying structure. Swift needed a way to let developers write linear, statement-like sequences that the compiler could automatically fold into a single nested value, making DSLs readable without sacrificing type safety.
THE MENTAL MODEL: Think of a result builder as a translator you install into the compiler. You write plain-looking code with sequential statements, if branches, and loops, and the builder translates that surface syntax into a series of static method calls that construct a final value. It is not runtime magic; it is a source-to-source rewrite that happens during type checking.
HOW IT WORKS: You define a struct or enum and mark it with the @resultBuilder attribute. Inside, you implement static methods such as buildBlock, buildOptional, buildEither, buildArray, and buildPartialBlock. When the compiler sees a function annotated with your builder, it transforms the body according to these rules. Multiple adjacent expressions become arguments to buildBlock. If statements become calls to buildOptional or buildEither depending on their shape. For loops become buildArray calls that collect iterations into a combined result. The compiler performs this rewrite silently, so the generated code never appears in your source.
WHEN TO USE IT: Reach for result builders when you are designing a domain-specific language where users declare structure through sequential syntax. Common examples include view hierarchies, HTML or XML generators, database query builders, and structured test assertions. They shine whenever the visual layout of the code should mirror the logical structure of the data.
WHEN NOT TO USE IT: Avoid result builders when ordinary function composition or initializer chaining is clear enough, because the implicit rewrite adds cognitive overhead and complicates debugging. They are also a poor fit for control flows that require early returns, throws inside the builder body, or break and continue statements, since the builder protocol may not support them and the resulting error messages can be opaque. If your DSL users need to reason about evaluation order or side effects precisely, explicit builders are safer.
ONE CANONICAL EXAMPLE: SwiftUI's ViewBuilder is the most widely known result builder. A VStack body containing a Text and a Button looks like two sequential statements, but the compiler rewrites it into ViewBuilder.buildBlock(Text(...), Button(...)). If you wrap one of those views in an if statement, the compiler injects ViewBuilder.buildEither to handle the conditional branch, preserving the same declarative feel while producing a strongly typed view graph.
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.