Step 1 of 4 · Reading · ~2 min
Learn
Builders and Opaque Types
Result builders are the compiler trick behind SwiftUI's view DSL. Written out, this block is not an array literal and not a series of ignored expressions:
VStack {
Text("Hello")
Image("icon")
}
Each statement in the closure is collected and handed to a builder type, which combines them into one value. The closure never "returns" anything — the compiler rewrites it into a call chain for you.
The attribute has two spellings, and your grader takes the older one
The feature shipped as the underscored @_functionBuilder in Swift 5.1 and was renamed to @resultBuilder when it was formally accepted in Swift 5.4. This course's grader is Swift 5.2, so:
@_functionBuilder // builds here
@resultBuilder // error: unknown attribute 'resultBuilder'
That is not a typo in the exercise. Real code you write today on a current toolchain uses @resultBuilder; the semantics below are identical under either spelling.
Defining a builder
@_functionBuilder
struct StringBuilder {
static func buildBlock(_ components: String...) -> String {
return components.joined(separator: " ")
}
}
func sentence(@StringBuilder _ build: () -> String) -> String { build() }
let s = sentence {
"Hello,"
"world!"
}
print(s) // Hello, world!
buildBlock is the only required method. Marking the parameter @StringBuilder is what makes the caller's closure a builder block; marking a function declaration applies it to that function's own body instead.
The other build methods, and what each one unlocks
You implement only what your DSL needs — each method switches on a piece of Swift syntax inside the block:
buildBlock(_:)— required; merges the statements of a block.buildOptional(_:)— enables a bareifwith noelse.buildEither(first:)/buildEither(second:)— both are needed to enableif/else.buildArray(_:)— enablesfor ... inloops.buildExpression(_:)— pre-processes each individual expression, letting a block accept types other than the final result type.buildFinalResult(_:)— one last transform on the way out.
Leave buildEither out and an if/else inside the block is a compile error, not a silent no-op. That is the usual first surprise: the block looks like ordinary Swift, but only the constructs your builder supports are legal in it.
Where they are used
SwiftUI views are the famous case; Swift's Regex Builders (5.7+), Vapor and Plot's HTML builders, and most test-matcher DSLs are built the same way. The pattern earns its keep whenever a caller should describe what they want as a list of declarations rather than assembling it imperatively.
Your exercise
You will write a builder whose buildBlock joins its parts with a single space, then use it on a function parameter. The mistake the grader catches is the separator: joining with "" or with a comma produces output that does not match, and remember the attribute must be @_functionBuilder to compile here.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…