# Result Builders in SwiftUI: How Declarative View Code Works

Source: https://devdocs10.com/articles/swift/swift-result-builders-swiftui  
By Murat Bo · Published 2026-06-10 · DevDocs10 (https://devdocs10.com)

SwiftUI result builders are the feature that lets you write view code as a clean, nested description instead of manually assembling arrays or calling build methods yourself. They are a big reason SwiftUI feels readable, but they also explain several common errors beginners hit when writing `body` code.

> **Quick answer:** SwiftUI uses a result builder, mainly `ViewBuilder`, to convert the expressions inside a view's `body` into a single view tree. That is why you can write multiple child views, `if` statements, and `ForEach` directly inside a view without returning each child manually.

> **Difficulty:** Intermediate
>
> **You'll understand this better if you know:** basic Swift functions, closures, and how SwiftUI views use the `body` property.

## 1. What Is Result Builders in SwiftUI?

A result builder is a Swift language feature that transforms a sequence of statements inside a closure or computed property into one combined result. In SwiftUI, this is used to let view-building code look like a normal block of declarations while Swift turns it into a single `View` value.

- It lets you write multiple views in one `body` property.
- It supports conditional content such as `if` and `if/else`.
- It supports loops such as `ForEach` when building repeated content.
- It hides the builder mechanics so the code reads like a UI description.

In SwiftUI, the built-in result builder you see most often is `ViewBuilder`. You do not usually call it directly, but it powers the syntax inside many SwiftUI APIs.

## 2. Why Result Builders Matter

Without result builders, SwiftUI view code would be much more verbose. You would need to manually combine child views, return a single expression from each branch, or build data structures before showing them in the interface.

Result builders matter because they make declarative UI practical:

- They reduce boilerplate in view code.
- They make nested layouts easier to scan.
- They allow SwiftUI to support conditionals and repeated content in a natural style.
- They help SwiftUI keep a strong type system while still feeling flexible.

They are especially useful in UI code, where composition matters more than imperative step-by-step control flow. In most app screens, they are the feature that makes SwiftUI look clean instead of cluttered.

## 3. Basic Syntax

The simplest way to think about a result builder is this: Swift takes several statements inside a special context and combines them into one final value. In SwiftUI, that final value is usually a view hierarchy.

### Minimal SwiftUI example

The `body` property below is not manually returning a tuple or array. SwiftUI's builder transforms the block into a single composed view.

```swift
import SwiftUI

struct WelcomeView: View {
    var body: some View {
        Text("Welcome")
        Text("Build interfaces declaratively.")
    }
}
```

This works because `body` is evaluated in a result-builder context. The two `Text` views are combined into a single result.

### Why this looks different from a normal function

In an ordinary Swift function, multiple standalone expressions are not allowed as the return value. A result builder changes the rules inside that closure or property so Swift can collect and combine them.

That is why this is valid in SwiftUI, but would be invalid in a plain function without a builder context.

## 4. Step-by-Step Examples

### Example 1: Multiple child views in a stack

Stacks are one of the most common places you see result builders. Each child view inside the stack initializer is collected and combined automatically.

```swift
import SwiftUI

struct ProfileHeader: View {
    var body: some View {
        VStack {
            Text("Ava Patel")
                .font(.title)
            Text("iOS Developer")
                .foregroundStyle(.secondary)
        }
    }
}
```

The `VStack` initializer uses a result builder so each child view becomes part of the stack content. You write layout code as a list of children rather than assembling them manually.

### Example 2: Conditional view content

Result builders are also what let you use control flow in `body`. SwiftUI can include one branch or another depending on state.

```swift
import SwiftUI

struct StatusView: View {
    let isOnline: Bool

    var body: some View {
        VStack {
            if isOnline {
                Text("Online")
                    .foregroundStyle(.green)
            } else {
                Text("Offline")
                    .foregroundStyle(.red)
            }
        }
    }
}
```

The builder keeps the code readable while still enforcing that the result is a valid SwiftUI view. Each branch must produce compatible view types under the hood.

### Example 3: Repeated content with ForEach

Repeated elements are often created inside a result-builder block. `ForEach` returns a view that represents a dynamic list of child views.

```swift
import SwiftUI

struct TagList: View {
    let tags = ["Swift", "SwiftUI", "Xcode"]

    var body: some View {
        HStack {
            ForEach(tags, id: \.self) { tag in
                Text(tag)
                    .padding(.horizontal, 8)
                    .padding(.vertical, 4)
            }
        }
    }
}
```

The builder is not creating the repetition itself. Instead, it allows the `ForEach` view to sit naturally inside the hierarchy and generate many child views from data.

### Example 4: Optional content with a placeholder

You can use builder-friendly control flow to show optional content only when a value exists.

```swift
import SwiftUI

struct AvatarSummary: View {
    let nickname: String?

    var body: some View {
        VStack {
            Text("Account")
            if let nickname {
                Text("Nickname: " + nickname)
            } else {
                Text("No nickname set")
                    .foregroundStyle(.secondary)
            }
        }
    }
}
```

This example shows how result builders make optional UI branches simple. You can express the logic directly in the view tree instead of precomputing strings or separate subviews first.

## 5. Practical Use Cases

Result builders show up anywhere SwiftUI needs a tree of child views. Common real-world uses include:

- Building screen layouts with `VStack`, `HStack`, `ZStack`, and `Grid`.
- Defining navigation content and conditional destination views.
- Rendering list sections with headers, rows, and empty states.
- Creating reusable custom container views that accept builder-based content.
- Showing or hiding interface elements based on state.
- Assembling toolbars, menus, and form sections in SwiftUI APIs that use builders internally.

Outside of SwiftUI, you may also see result builders in custom DSLs or libraries that want a declarative style. But in app development, SwiftUI is the main place most developers encounter them.

## 6. Common Mistakes

### Mistake 1: Expecting every line to behave like a normal function return

SwiftUI builders accept multiple child expressions, but they still require each expression to produce a valid view. Some beginners try to mix arbitrary statements into the view block.

**Problem:** A result builder block is not a general-purpose scripting area. Statements that do not produce a view can cause type-checking errors or make the block invalid.

```swift
import SwiftUI

struct BrokenView: View {
    var body: some View {
        Text("Hello")
        print("Loaded")
    }
}
```

**Fix:** Move side effects outside the builder block, for example into `onAppear` or a separate method.

```swift
import SwiftUI

struct FixedView: View {
    var body: some View {
        Text("Hello")
            .onAppear {
                print("Loaded")
            }
    }
}
```

The corrected version works because the builder block stays focused on producing views, not executing unrelated statements.

### Mistake 2: Returning mismatched types in different branches

Result builders often hide the complexity of conditionals, but the branches still need to be compatible. SwiftUI usually solves this through type erasure inside the builder, but not every custom builder does.

**Problem:** If a builder or API does not support incompatible branches, Swift may report that the branches have mismatched types.

```swift
import SwiftUI

struct BranchMismatchView: View {
    let showTitle: Bool

    var body: some View {
        if showTitle {
            Text("Title")
        } else {
            Image(systemName: "star")
        }
    }
}
```

**Fix:** Wrap the branches in a container or convert them into a common layout so the overall result is consistent.

```swift
import SwiftUI

struct BranchFixedView: View {
    let showTitle: Bool

    var body: some View {
        VStack {
            if showTitle {
                Text("Title")
            } else {
                Image(systemName: "star")
            }
        }
    }
}
```

The container gives the builder a consistent outer view so the conditional content can vary safely inside it.

### Mistake 3: Forgetting that control flow must still return content

Another common issue is assuming an `if` without an `else` always behaves like a normal optional block. In a builder context, that is allowed only when the builder supports missing branches.

**Problem:** In contexts that require every branch to contribute a result, a missing return path can trigger errors such as a closure not returning a value on all paths.

```swift
import SwiftUI

struct IncompleteView: View {
    let showMessage: Bool

    var body: some View {
        VStack {
            if showMessage {
                Text("Ready")
            }
        }
    }
}
```

**Fix:** Add an `else` branch or make the content explicitly optional in a way the builder accepts.

```swift
import SwiftUI

struct CompleteView: View {
    let showMessage: Bool

    var body: some View {
        VStack {
            if showMessage {
                Text("Ready")
            } else {
                Text("Waiting")
                    .foregroundStyle(.secondary)
            }
        }
    }
}
```

The fixed version works because the builder always has a complete set of branches to combine.

## 7. Best Practices

### Practice 1: Keep builder blocks focused on UI composition

A result builder should describe what the interface looks like, not perform unrelated work. If a block becomes too busy, move logic into computed properties or helper functions.

```swift
import SwiftUI

struct FocusedView: View {
    let count: Int

    private var statusText: String {
        count > 0 ? "Items available" : "No items"
    }

    var body: some View {
        VStack {
            Text(statusText)
            Text("Count: \(count)")
        }
    }
}
```

This keeps the builder readable and makes the UI easier to maintain.

### Practice 2: Use small helper views for repeated structure

If a builder block repeats the same layout pattern, extract a child view instead of duplicating structure inside the builder.

```swift
import SwiftUI

struct InfoRow: View {
    let label: String
    let value: String

    var body: some View {
        HStack {
            Text(label)
            Spacer()
            Text(value)
                .foregroundStyle(.secondary)
        }
    }
}
```

Reusable views reduce builder complexity and make nested layouts easier to test and reuse.

### Practice 3: Prefer clear conditional branches over deeply nested logic

Result builders make conditional UI easy, but deeply nested conditions can become hard to scan. If the logic gets complicated, break it into separate views or computed subviews.

```swift
import SwiftUI

struct AccountBadge: View {
    let isPremium: Bool
    let isVerified: Bool

    var body: some View {
        HStack {
            if isPremium {
                Text("Premium")
            }

            if isVerified {
                Text("Verified")
            }
        }
    }
}
```

Short, separate conditions are easier to understand than a single complicated branch tree inside the builder.

## 8. Limitations and Edge Cases

- Result builders do not mean every Swift statement is allowed inside a view block. The content still has to fit the builder's rules.
- Type inference can become slow or confusing in very large view hierarchies, especially with complex conditionals and generic containers.
- Not every custom API uses `ViewBuilder`. Some closures are plain closures and require a normal return value.
- Different builders can support different control-flow features. Just because `VStack` accepts an `if` does not mean every builder does.
- Builder-generated code can be harder to reason about when branches are deeply nested, because the final type is constructed implicitly.
- Errors sometimes point at the wrong line in large builder blocks, so the visible compiler message may feel less direct than expected.

One subtle edge case is that the view result still has a static type, even if you do not write it explicitly. The compiler must understand the shape of the builder output at compile time.

## 9. Practical Mini Project

Let's build a small `TaskCard` view that uses a result builder naturally. It shows a title, optional priority, and a status row that changes based on completion state.

```swift
import SwiftUI

struct TaskCard: View {
    let title: String
    let priority: String?
    let isDone: Bool

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(title)
                .font(.headline)

            if let priority {
                Text("Priority: " + priority)
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }

            if isDone {
                Label("Completed", systemImage: "checkmark.circle.fill")
                    .foregroundStyle(.green)
            } else {
                Label("In progress", systemImage: "clock")
                    .foregroundStyle(.orange)
            }
        }
        .padding()
        .background(.thinMaterial)
        .clipShape(RoundedRectangle(cornerRadius: 12))
    }
}

struct ContentView: View {
    var body: some View {
        TaskCard(title: "Write documentation", priority: "High", isDone: false)
            .padding()
    }
}
```

This mini project shows the builder in a realistic way: the card reads like a UI description, but SwiftUI still turns it into a strongly typed view hierarchy.

## 10. Key Points

- Result builders let SwiftUI collect multiple statements and combine them into one final view value.
- `ViewBuilder` is the most common result builder in SwiftUI.
- They make stacks, conditionals, and repeated content feel declarative and readable.
- Builder blocks still obey Swift type rules, even though they look more flexible than normal functions.
- When a view block becomes hard to read, move logic into helper properties or smaller views.

## 11. Practice Exercise

Build a small SwiftUI view that uses a result builder to show one of two messages and an optional subtitle.

- Create a view with a `title` string.
- Show a subtitle only when one is provided.
- Show `Welcome back` when `isSignedIn` is true.
- Show `Please sign in` when `isSignedIn` is false.

**Expected output:** A centered SwiftUI card with a title, an optional subtitle, and one of two status messages depending on sign-in state.

**Hint:** Put the conditional UI directly inside a `VStack` so the builder can assemble the branches for you.

**Solution:**

```swift
import SwiftUI

struct SignInCard: View {
    let title: String
    let subtitle: String?
    let isSignedIn: Bool

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(title)
                .font(.title2)
                .bold()

            if let subtitle {
                Text(subtitle)
                    .foregroundStyle(.secondary)
            }

            if isSignedIn {
                Text("Welcome back")
                    .foregroundStyle(.green)
            } else {
                Text("Please sign in")
                    .foregroundStyle(.orange)
            }
        }
        .padding()
    }
}

struct ContentView: View {
    var body: some View {
        SignInCard(title: "Account Status", subtitle: "Last updated just now", isSignedIn: true)
            .padding()
    }
}
```

This solution works because the builder combines the title, optional subtitle, and conditional status text into one cohesive layout.

## 12. Final Summary

Result builders are the feature that makes SwiftUI's declarative syntax possible. They let you write multiple child views, conditional branches, and repeated content inside a single block while Swift handles the transformation into one final view value.

In practice, that means less boilerplate and more readable UI code, but it also means you need to think in terms of builder rules. Every expression still has to fit the builder's expectations, and larger blocks are easier to maintain when you extract subviews or computed properties.

If you want to go further, the next useful topic is how SwiftUI's `ViewBuilder` works with opaque return types like `some View`, because those two features are closely connected in real SwiftUI code.
