SwiftUI.cc
Snippets
State UIView CompositionAdvanced

SwiftUI @ViewBuilder and some View Explained

Why body returns 'some View', how @ViewBuilder turns multiple statements into one view tree, and what opaque types buy SwiftUI's diffing — the syntax behind every view you write.

5 min readUpdated 2026-06
Builder-powered reusable card
import SwiftUI

struct InfoCard<Content: View>: View {
    let title: String
    @ViewBuilder var content: Content

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(title).font(.headline)
            content
        }
        .padding()
        .background(.quaternary.opacity(0.4), in: .rect(cornerRadius: 14))
    }
}

struct Demo: View {
    @State private var detailed = false

    var body: some View {
        InfoCard(title: "Status") {
            Label("Online", systemImage: "checkmark.circle")
            if detailed {
                Text("Latency 24 ms").font(.caption)
            }
        }
        .padding()
    }
}

ViewBuilder explained preview

The type you never write

A modest screen's real type looks like VStack<TupleView<(Text, ModifiedContent<Image, _PaddingLayout>, …)>>. some View lets body promise "one concrete view type" without spelling it — the compiler still knows the exact type, which SwiftUI uses to diff updates structurally instead of re-creating trees.

What @ViewBuilder actually does

Closures and properties marked @ViewBuilder get their statements rewritten: several views become a TupleView, an if/else becomes _ConditionalContent, an if without else wraps in Optional. That is why you can 'just list views' inside a VStack — and why both branches of a condition can be different view types without erasure.

@ViewBuilder
private var statusBadge: some View {
    if isOnline {
        Label("Online", systemImage: "checkmark.circle")
    } else {
        Label("Offline", systemImage: "xmark.circle")
    }
}

Why not AnyView?

AnyView erases type structure, so SwiftUI can no longer see that 'the same Text changed' versus 'a different view appeared' — transitions degrade and diffing coarsens. Builders preserve identity through branches. AnyView remains a legitimate tool for heterogeneous collections, but it is the last resort, not the fix for a builder you forgot.

Accepting view closures

Generic content parameters (<Content: View> plus @ViewBuilder var content: Content) are how VStack, GroupBox, and your own containers accept arbitrary children — the standard shape for reusable chrome.

Common mistakes

  • AnyView sprinkled to fix 'branches return different types' that @ViewBuilder solves.
  • Doing data work inside body instead of returning views.
  • Giant builder expressions the compiler struggles to type-check — extract subviews.

Related reference