Bird
Raised Fist0
Swiftprogramming~10 mins

@resultBuilder attribute in Swift - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - @resultBuilder attribute
Start Function Call
↓
Collect All Statements in Closure
↓
Call buildBlock with All Components
↓
buildBlock Joins Components
↓
Return Final Result
↓
Function Returns Built Result
The @resultBuilder attribute collects all statements in a closure and passes them as arguments to buildBlock in a single call, which combines them into one result.
Execution Sample
Swift
import Foundation

@resultBuilder
struct StringBuilder {
    static func buildBlock(_ components: String...) -> String {
        components.joined(separator: ", ")
    }
}

func makeSentence(@StringBuilder content: () -> String) -> String {
    "Sentence: " + content()
}

let sentence = makeSentence {
    "Hello"
    "world"
    "from"
    "@resultBuilder"
}
print(sentence)
This code uses @resultBuilder to pass all strings from the closure to buildBlock, which joins them into one string.
Execution Table
StepClosure StatementsbuildBlock InputbuildBlock OutputCombined Result
1"Hello" "world" "from" "@resultBuilder"["Hello", "world", "from", "@resultBuilder"]"Hello, world, from, @resultBuilder""Hello, world, from, @resultBuilder"
2Return from makeSentenceN/A"Sentence: Hello, world, from, @resultBuilder""Sentence: Hello, world, from, @resultBuilder"
💡 All closure statements collected at once and passed to buildBlock, which combines them before the function returns.
Variable Tracker
VariableBefore ClosureAfter CollectionAfter buildBlockFinal
components[]["Hello", "world", "from", "@resultBuilder"]["Hello", "world", "from", "@resultBuilder"]["Hello", "world", "from", "@resultBuilder"]
content() result"""""Hello, world, from, @resultBuilder""Hello, world, from, @resultBuilder"
sentence""N/AN/A"Sentence: Hello, world, from, @resultBuilder"
Key Moments - 3 Insights
How does @resultBuilder handle multiple statements in the closure?
@resultBuilder collects all statements and passes them directly to buildBlock as variadic arguments in one call, as shown in execution_table step 1.
What does the buildBlock function receive and return?
buildBlock receives an array of all String components from the closure and returns a single joined String.
Why is the output a single prefixed string?
makeSentence wraps the single result from the builder closure with 'Sentence: ', as in execution_table step 2.
Visual Quiz - 3 Questions
Test your understanding
In the execution_table step 1, what is passed to buildBlock?
A["Hello"]
B["Hello", "world", "from", "@resultBuilder"]
C["Hello", "world"]
D[]
💡 Hint
Check the buildBlock Input column at step 1; all statements are collected at once.
How many times is buildBlock called for this closure?
A4
B2
C1
D0
💡 Hint
Review the execution_table; there's one call with all components.
What happens without a buildBlock method in the result builder?
ACompilation error; required to combine statements.
BReturns only the last statement.
CReturns an array automatically.
DNo change; Swift joins strings.
💡 Hint
@resultBuilder relies on buildBlock (and others) to define how to combine.
Concept Snapshot
@resultBuilder attribute lets you write multiple statements in a closure
and combines them into one result using builder methods like buildBlock.
You define a struct/class with static buildBlock methods.
The closure's statements become inputs to buildBlock.
buildBlock returns the final combined value.
Used to create DSL-like syntax in Swift.
Full Transcript
The @resultBuilder attribute in Swift allows a function to accept a closure with multiple statements that get collected and passed to buildBlock in one call to produce a single result. In the example, all four strings are passed to buildBlock, which joins them. The execution table shows the single collection and combination step. The variable tracker tracks components from collection to final sentence. Key moments explain the all-at-once collection and buildBlock's role. Visual quizzes test grasp of the process. This creates clean, declarative syntax.

Practice

(1/5)
1. What is the main purpose of the @resultBuilder attribute in Swift?
easy
A. To combine multiple expressions into a single result in a clean way
B. To declare a new type of variable
C. To handle errors automatically
D. To create a new thread for concurrent execution

Solution

  1. Step 1: Understand the role of @resultBuilder

    @resultBuilder is used to combine multiple code lines or expressions into one result, making code cleaner and easier to write.
  2. Step 2: Compare options with the purpose

    Options B, C, and D describe unrelated features. Only To combine multiple expressions into a single result in a clean way correctly describes the purpose of @resultBuilder.
  3. Final Answer:

    To combine multiple expressions into a single result in a clean way -> Option A
  4. Quick Check:

    @resultBuilder purpose = combine expressions [OK]
Hint: Remember: @resultBuilder combines code lines into one result [OK]
Common Mistakes:
  • Confusing @resultBuilder with error handling
  • Thinking it creates new variables
  • Assuming it manages concurrency
2. Which of the following is the correct way to declare a custom result builder in Swift?
easy
A. @resultBuilder class MyBuilder { func buildBlock(_ components: Int...) -> Int { components.reduce(0, +) } }
B. @resultBuilder struct MyBuilder { static func buildBlock(_ components: Int...) -> Int { components.reduce(0, +) } }
C. @resultBuilder func MyBuilder(_ components: Int...) -> Int { components.reduce(0, +) }
D. struct MyBuilder { func buildBlock(_ components: Int...) -> Int { components.reduce(0, +) } }

Solution

  1. Step 1: Identify correct @resultBuilder declaration

    A custom result builder must be declared as a struct or enum with the @resultBuilder attribute and static buildBlock method.
  2. Step 2: Check each option

    @resultBuilder struct MyBuilder { static func buildBlock(_ components: Int...) -> Int { components.reduce(0, +) } } correctly uses @resultBuilder on a struct and static buildBlock method. struct MyBuilder { func buildBlock(_ components: Int...) -> Int { components.reduce(0, +) } } misses @resultBuilder and static keyword. @resultBuilder func MyBuilder(_ components: Int...) -> Int { components.reduce(0, +) } wrongly applies @resultBuilder to a function. @resultBuilder class MyBuilder { func buildBlock(_ components: Int...) -> Int { components.reduce(0, +) } } uses a class, which is not allowed.
  3. Final Answer:

    @resultBuilder struct MyBuilder { static func buildBlock(_ components: Int...) -> Int { components.reduce(0, +) } } -> Option B
  4. Quick Check:

    Custom builder = struct + @resultBuilder + static buildBlock [OK]
Hint: Use @resultBuilder on struct with static buildBlock method [OK]
Common Mistakes:
  • Forgetting static keyword on buildBlock
  • Applying @resultBuilder to functions
  • Using class instead of struct or enum
3. Given the following code, what will be printed?
 @resultBuilder struct SumBuilder {
    static func buildBlock(_ components: Int...) -> Int {
        components.reduce(0, +)
    }
}

func sum(@SumBuilder _ content: () -> Int) -> Int {
    content()
}

let result = sum {
    2
    3
    5
}
print(result)
medium
A. Error: Missing return
B. 235
C. 0
D. 10

Solution

  1. Step 1: Understand buildBlock behavior

    The buildBlock method receives all integers (2, 3, 5) and sums them using reduce(0, +), resulting in 10.
  2. Step 2: Check function call and output

    The sum function calls content(), which triggers the builder and returns 10. print(result) outputs 10.
  3. Final Answer:

    10 -> Option D
  4. Quick Check:

    Sum of 2+3+5 = 10 [OK]
Hint: Sum builder adds all values inside closure [OK]
Common Mistakes:
  • Thinking values concatenate as strings
  • Expecting a syntax error
  • Missing that buildBlock returns sum
4. Identify the error in this custom result builder code:
 @resultBuilder struct MyBuilder {
    func buildBlock(_ components: String...) -> String {
        components.joined(separator: ", ")
    }
}

func buildString(@MyBuilder _ content: () -> String) -> String {
    content()
}

let result = buildString {
    "Hello"
    "World"
}
print(result)
medium
A. Missing return statement in buildBlock
B. Cannot use String type in result builder
C. buildBlock must be static
D. Function buildString should not have @MyBuilder

Solution

  1. Step 1: Check buildBlock method declaration

    In a result builder, buildBlock must be a static method. Here it is an instance method (missing static).
  2. Step 2: Verify other parts

    Return statement is present, String type is allowed, and function parameter correctly uses @MyBuilder.
  3. Final Answer:

    buildBlock must be static -> Option C
  4. Quick Check:

    buildBlock static keyword required [OK]
Hint: Always make buildBlock static in result builders [OK]
Common Mistakes:
  • Omitting static keyword on buildBlock
  • Confusing return statement presence
  • Thinking @resultBuilder restricts types
5. You want to create a custom result builder that builds a dictionary from key-value pairs written inside a closure. Which of the following implementations correctly uses @resultBuilder to achieve this?
hard
A. @resultBuilder struct DictBuilder { static func buildBlock(_ components: (String, Int)...) -> [String: Int] { var dict = [String: Int]() for (key, value) in components { dict[key] = value } return dict } } func buildDict(@DictBuilder _ content: () -> [String: Int]) -> [String: Int] { content() } let dict = buildDict { ("a", 1) ("b", 2) ("c", 3) } print(dict)
B. @resultBuilder struct DictBuilder { static func buildBlock(_ components: [String: Int]...) -> [String: Int] { components.reduce([:]) { $0.merging($1) { $1 } } } } func buildDict(@DictBuilder _ content: () -> [String: Int]) -> [String: Int] { content() } let dict = buildDict { ["a": 1] ["b": 2] ["c": 3] } print(dict)
C. @resultBuilder struct DictBuilder { static func buildBlock(_ components: String...) -> [String: Int] { [:] } } func buildDict(@DictBuilder _ content: () -> [String: Int]) -> [String: Int] { content() } let dict = buildDict { "a:1" "b:2" "c:3" } print(dict)
D. struct DictBuilder { static func buildBlock(_ components: (String, Int)...) -> [String: Int] { var dict = [String: Int]() for (key, value) in components { dict[key] = value } return dict } } func buildDict(@DictBuilder _ content: () -> [String: Int]) -> [String: Int] { content() } let dict = buildDict { ("a", 1) ("b", 2) ("c", 3) } print(dict)

Solution

  1. Step 1: Check for @resultBuilder attribute and static buildBlock

    @resultBuilder struct DictBuilder { static func buildBlock(_ components: (String, Int)...) -> [String: Int] { var dict = [String: Int]() for (key, value) in components { dict[key] = value } return dict } } func buildDict(@DictBuilder _ content: () -> [String: Int]) -> [String: Int] { content() } let dict = buildDict { ("a", 1) ("b", 2) ("c", 3) } print(dict) correctly declares @resultBuilder on a struct and has a static buildBlock method that combines tuples into a dictionary.
  2. Step 2: Verify code correctness and usage

    @resultBuilder struct DictBuilder { static func buildBlock(_ components: [String: Int]...) -> [String: Int] { components.reduce([:]) { $0.merging($1) { $1 } } } } func buildDict(@DictBuilder _ content: () -> [String: Int]) -> [String: Int] { content() } let dict = buildDict { ["a": 1] ["b": 2] ["c": 3] } print(dict) uses arrays of dictionaries which works but is less direct than tuples; @resultBuilder struct DictBuilder { static func buildBlock(_ components: String...) -> [String: Int] { [:] } } func buildDict(@DictBuilder _ content: () -> [String: Int]) -> [String: Int] { content() } let dict = buildDict { "a:1" "b:2" "c:3" } print(dict) returns empty dictionary ignoring input; struct DictBuilder { static func buildBlock(_ components: (String, Int)...) -> [String: Int] { var dict = [String: Int]() for (key, value) in components { dict[key] = value } return dict } } func buildDict(@DictBuilder _ content: () -> [String: Int]) -> [String: Int] { content() } let dict = buildDict { ("a", 1) ("b", 2) ("c", 3) } print(dict) misses @resultBuilder attribute, so it won't work as a builder.
  3. Final Answer:

    Option A correctly implements a dictionary builder using @resultBuilder -> Option A
  4. Quick Check:

    @resultBuilder + static buildBlock + tuple input = correct dictionary builder [OK]
Hint: Use tuples and static buildBlock with @resultBuilder for dictionary [OK]
Common Mistakes:
  • Forgetting @resultBuilder attribute
  • Returning empty or wrong type
  • Using non-static buildBlock
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard