Result builders (formerly known as function builders) are a powerful Swift feature that enables you to create elegant domain-specific languages (DSLs). They're the magic behind SwiftUI's declarative syntax and can be used to build your own custom DSLs. This is an advanced Swift feature (requires Swift 5.4+; platform availability depends on the APIs you use, such as SwiftUI). Familiarity with generics and protocols is recommended.
What are Result Builders?
Result builders are types annotated with @resultBuilder that transform multiple statements into a single combined result. They enable natural, declarative syntax for constructing complex values.
Basic Result Builder
Here's a simple result builder for building strings:
@resultBuilder
struct StringBuilder {
static func buildBlock(_ components: String...) -> String {
components.joined(separator: "\n")
}
}
@StringBuilder
func makeGreeting() -> String {
"Hello"
"World"
"from Swift!"
}
print(makeGreeting())
// Output:
// Hello
// World
// from Swift!
Supporting Control Flow
Add support for if statements:
@resultBuilder
struct HTMLBuilder {
static func buildBlock(_ components: String...) -> String {
components.joined()
}
static func buildOptional(_ component: String?) -> String {
component ?? ""
}
static func buildEither(first component: String) -> String {
component
}
static func buildEither(second component: String) -> String {
component
}
}
@HTMLBuilder
func buildPage(isLoggedIn: Bool) -> String {
"<html>"
"<body>"
if isLoggedIn {
"<h1>Welcome back!</h1>"
} else {
"<h1>Please log in</h1>"
}
"</body>"
"</html>"
}
Supporting Arrays and Loops
Add support for for loops:
extension HTMLBuilder {
static func buildArray(_ components: [String]) -> String {
components.joined()
}
}
@HTMLBuilder
func buildList(items: [String]) -> String {
"<ul>"
for item in items {
"<li>\(item)</li>"
}
"</ul>"
}
Real-World Example: URL Builder
Here's a practical result builder for constructing URLs:
@resultBuilder
struct URLComponentBuilder {
static func buildBlock(_ components: URLQueryItem...) -> [URLQueryItem] {
components
}
static func buildOptional(_ component: [URLQueryItem]?) -> [URLQueryItem] {
component ?? []
}
static func buildArray(_ components: [[URLQueryItem]]) -> [URLQueryItem] {
components.flatMap { $0 }
}
}
struct URLBuilder {
let baseURL: URL
func build(@URLComponentBuilder _ builder: () -> [URLQueryItem]) -> URL? {
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: true)
components?.queryItems = builder()
return components?.url
}
}
// Usage
let builder = URLBuilder(baseURL: URL(string: "https://api.example.com/search")!)
let url = builder.build {
URLQueryItem(name: "q", value: "swift")
URLQueryItem(name: "limit", value: "10")
if includeImages {
URLQueryItem(name: "images", value: "true")
}
}
Custom View Builder
Create a custom view-like builder:
protocol View {
var body: String { get }
}
@resultBuilder
struct ViewBuilder {
static func buildBlock(_ components: View...) -> [View] {
components
}
}
struct Text: View {
let content: String
var body: String { "<p>\(content)</p>" }
}
struct VStack: View {
let children: [View]
init(@ViewBuilder content: () -> [View]) {
self.children = content()
}
var body: String {
"<div>\(children.map { $0.body }.joined())</div>"
}
}
// Usage
let view = VStack {
Text("Hello")
Text("World")
}
Use Cases
Result builders are perfect for:
-
Creating DSLs for configuration
-
Building HTML/XML
-
Constructing complex data structures
-
Query builders
-
Test specifications
-
Custom UI frameworks
Wrap up
Result builders enable elegant, declarative APIs in Swift. Understanding how they work helps you use SwiftUI more effectively and create your own domain-specific languages.
Resources: