Skip to main content
The @resultBuilder attribute is a compiler feature that allows a custom type to intercept and transform a sequence of statements within a closure into a single, aggregated value. By applying this attribute to a struct, class, or enum, you define a domain-specific language (DSL) where the Swift compiler translates standard control-flow syntax into a series of static method calls on the builder type. When a closure or function is annotated with a result builder, the compiler performs an Abstract Syntax Tree (AST) transformation. It parses the sequential statements, conditionals, and loops inside the block, and replaces them with calls to specific build* methods defined on the builder type.

Builder Type Structure

A result builder is defined by applying the @resultBuilder attribute to a type and implementing at least one block-building static method (buildBlock or buildPartialBlock). The type operates entirely through static methods; it is never instantiated. To demonstrate a fully compilable implementation, the following examples rely on these concrete types:
Here is the foundational structure of a result builder. A valid builder must contain at least one method to combine components:

The build Method Contract

The compiler looks for specific static methods to handle different Swift language constructs. You only need to implement the methods corresponding to the syntax you want your builder to support. The following extensions build upon the AbstractBuilder defined above to support a complete DSL.
  • buildBlock(_:) (Legacy / Variadic Combination) Translates sequential statements in a block. For homogeneous builders (where all components share the same type), a single variadic parameter (Component...) handles any number of statements without arity limits. However, for heterogeneous builders (which preserve distinct types using generics, like <C0, C1>), developers historically had to provide multiple overloaded buildBlock methods for different arities (e.g., up to 10 arguments).
  • buildPartialBlock(first:) and buildPartialBlock(accumulated:next:) (Modern Combination, Swift 5.7+) Replaces the need for multiple buildBlock overloads in heterogeneous builders by folding components pairwise. The compiler passes the first component to buildPartialBlock(first:), and then iteratively passes the accumulated result and the next component to buildPartialBlock(accumulated:next:). This allows combining an arbitrary number of distinct types without artificial arity limits.
  • buildExpression(_:) (Optional) Lifts an expression into the builder’s internal Component type. If implemented, the compiler passes every raw expression through this method before passing it to the block-building methods. This allows the builder to accept types that differ from its internal representation.
  • buildOptional(_:) (Optional) Translates if statements that do not have an else branch.
  • buildEither(first:) and buildEither(second:) (Optional) Translates if-else statements and switch statements. The compiler wraps the execution path taken in either first or second to maintain a unified return type.
  • buildArray(_:) (Optional) Translates for...in loops. The compiler collects the results of each loop iteration into an array and passes it to this method.
  • buildLimitedAvailability(_:) (Optional) Translates if #available or if #unavailable blocks, allowing the builder to erase type information related to API availability.
  • buildFinalResult(_:) (Optional) Transforms the final accumulated Component into a different return type. If implemented, this is the last method called before the closure returns.

Compiler Transformation Mechanics

To understand the mechanics, observe how the Swift compiler translates standard syntax into builder method calls at compile time. Because AbstractBuilder now implements buildExpression, buildEither, and buildFinalResult, the following code is fully valid. Source Code:
Compiler-Transformed Code: (Note: This illustrates the transformation using buildBlock. If buildPartialBlock were implemented and preferred by the compiler, step 3 would use pairwise folding instead).

Application of the Attribute

Once defined, the @resultBuilder attribute can be applied in three primary locations:
  1. Function Declarations: Applied directly to a function or method, transforming its body.
  1. Closure Parameters: Applied to a closure parameter in a function signature, transforming any closure passed to that function.
  1. Computed Properties: Applied to a getter, transforming the property’s body.
Tired of Poor Swift Skills? Fix That With Deep Grasping!Learn More