The @resultBuilder attribute helps you create special functions that build complex values step-by-step using simple code blocks. It makes your code easier to read and write by letting you write multiple statements that combine into one result.
@resultBuilder attribute in Swift
@resultBuilder struct BuilderName { static func buildBlock(_ components: ComponentType...) -> ResultType { // Combine components into one result } // Optional: other build methods like buildIf, buildEither, buildArray }
The @resultBuilder attribute is placed before a struct or enum that defines how to combine code blocks.
You define static methods like buildBlock to tell Swift how to combine parts.
StringBuilder that joins multiple strings with spaces.@resultBuilder struct StringBuilder { static func buildBlock(_ components: String...) -> String { components.joined(separator: " ") } }
@resultBuilder in a function to combine strings written inside a closure.func makeString(@StringBuilder content: () -> String) -> String { content() } let result = makeString { "Hello" "world!" }
This program defines an @resultBuilder called ArrayBuilder that combines arrays of integers. The makeArray function uses it to build one array from multiple parts, including an if condition.
import Foundation @resultBuilder struct ArrayBuilder { static func buildBlock<T>(_ components: [T]...) -> [T] { components.flatMap { $0 } } static func buildIf<T>(_ component: [T]?) -> [T] { component ?? [] } } func makeArray(@ArrayBuilder content: () -> [Int]) -> [Int] { content() } let numbers = makeArray { [1, 2] if true { [3, 4] } [5] } print(numbers)
You can add special methods like buildIf to handle optional code blocks (like if statements).
Result builders let you write code that looks like normal statements but actually builds a value behind the scenes.
They are very useful for creating domain-specific languages (DSLs) inside Swift.
@resultBuilder helps combine multiple code lines into one result.
It makes code cleaner and easier to write for building complex data or UI.
You define how to combine parts by writing static methods inside a builder type.
Practice
@resultBuilder attribute in Swift?Solution
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.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.Final Answer:
To combine multiple expressions into a single result in a clean way -> Option AQuick Check:
@resultBuilder purpose = combine expressions [OK]
- Confusing @resultBuilder with error handling
- Thinking it creates new variables
- Assuming it manages concurrency
Solution
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.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.Final Answer:
@resultBuilder struct MyBuilder { static func buildBlock(_ components: Int...) -> Int { components.reduce(0, +) } } -> Option BQuick Check:
Custom builder = struct + @resultBuilder + static buildBlock [OK]
- Forgetting static keyword on buildBlock
- Applying @resultBuilder to functions
- Using class instead of struct or enum
@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)Solution
Step 1: Understand buildBlock behavior
The buildBlock method receives all integers (2, 3, 5) and sums them using reduce(0, +), resulting in 10.Step 2: Check function call and output
The sum function calls content(), which triggers the builder and returns 10. print(result) outputs 10.Final Answer:
10 -> Option DQuick Check:
Sum of 2+3+5 = 10 [OK]
- Thinking values concatenate as strings
- Expecting a syntax error
- Missing that buildBlock returns sum
@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)Solution
Step 1: Check buildBlock method declaration
In a result builder, buildBlock must be a static method. Here it is an instance method (missing static).Step 2: Verify other parts
Return statement is present, String type is allowed, and function parameter correctly uses @MyBuilder.Final Answer:
buildBlock must be static -> Option CQuick Check:
buildBlock static keyword required [OK]
- Omitting static keyword on buildBlock
- Confusing return statement presence
- Thinking @resultBuilder restricts types
Solution
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.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.Final Answer:
Option A correctly implements a dictionary builder using @resultBuilder -> Option AQuick Check:
@resultBuilder + static buildBlock + tuple input = correct dictionary builder [OK]
- Forgetting @resultBuilder attribute
- Returning empty or wrong type
- Using non-static buildBlock
Ready to prove it?
Pass the test and this pattern moves unknown
