Views and layout · core

Your first View

What some View actually means, why body is recomputed rather than mutated, and the mental shift from UIKit.

11 min read14 min practice0/2 exercises4 recall cards

By the end you will be able to

  • Write a View conforming type and explain every part of its declaration
  • Explain what "declarative" means in terms of what you write and what the framework does
  • Compose views from smaller views and pass data in
Guess firstAnswering before you read makes the explanation stick — even when you get it wrong.

In SwiftUI, when does the code inside var body: some View run?

The smallest possible view

struct Greeting: View {
    var body: some View {
        Text("Hello, world!")
    }
}

Four things, each worth naming:

  • struct — views are values, not objects. They are created and thrown away constantly, which is only cheap because they are structs.
  • : View — a protocol with one requirement: body.
  • var body: some View — a computed property returning some specific View type the caller does not need to know.
  • Text("Hello, world!") — itself a View. Everything in SwiftUI is a view.
SwiftUI preview
Run to see the preview.

Try it: change the text, swap .title for .largeTitle or .caption, change the colour to .orange. The preview updates as soon as you press Run.

What some View means

some View is an opaque return type. It says: this returns one specific type conforming to View, chosen at compile time, and I am not telling you which.

Why not just write View? The compiler refuses that outright (View has associated types), but the more useful question is what the alternative world would cost: any View erases the concrete type into a box, and a hierarchy of boxes is one SwiftUI can no longer see into — yet the tree of concrete types is exactly what it diffs cheaply ("still a VStack of Text over Image") and what the compiler optimises. some View splits the difference: the concrete type stays visible to the machinery, hidden only from you — so you can change the body without ever changing the signature.

The actual type piles up as you add modifiers. Add .padding() to a Text and the concrete type is already:

ModifiedContent<Text, _PaddingLayout>

— and each further modifier wraps another ModifiedContent layer around that. (A handful of Text-specific modifiers like .font are special-cased to return Text itself, but the general-purpose ones all wrap.) Nobody wants to write these types. some View is how you avoid it.

Declarative versus imperative

In UIKit you write instructions: create a label, set its font, add it to a subview, and later, when something changes, remember to update it. Every state change needs matching update code, and forgetting one is the classic UIKit bug.

In SwiftUI you write a function from state to appearance. When the state changes, SwiftUI re-runs the function. There is no update code to forget.

UIKit: describe the steps
let label = UILabel()
label.text = "Hello"
label.font = .preferredFont(
    forTextStyle: .title1)
label.textColor = .systemBlue
view.addSubview(label)
label.translatesAutoresizing... = false
NSLayoutConstraint.activate([...])

// Later, when the name changes:
label.text = "Hello, \(name)"
// (if you remember)
SwiftUI: describe the result
struct Greeting: View {
    let name: String

    var body: some View {
        Text("Hello, \(name)")
            .font(.title)
            .foregroundStyle(.blue)
    }
}

// Later, when the name changes:
// nothing — the view is recomputed.

The right-hand version has no update path to forget, because there is no update path. That is the whole trade: you give up direct control over when things happen and get correctness-by-construction in return.

Composing views

Views nest. A view's body can contain other views, including ones you wrote:

SwiftUI preview
Run to see the preview.

Badge is a view used inside ProfileRow, which is used inside the preview. Data flows down through initializers — Badge(text: role) — which is exactly the memberwise initializer you already know from structs.

#Preview and Xcode canvas

The #Preview macro declares a preview. In Xcode it renders live in the canvas beside your code and updates as you type. Here it renders in the phone frame beside the editor.

#Preview {
    ProfileRow(name: "Ada", role: "Author")
}

#Preview("Dark mode") {
    ProfileRow(name: "Ada", role: "Author")
        .preferredColorScheme(.dark)
}

Multiple previews with names let you check several states side by side, which is the fastest way to test edge cases — long names, empty strings, extreme dynamic type.

Predict, then runA wrong prediction you have committed to is worth more than a right answer you read.

Views are values, and modifiers return new views. What is the type relationship here?

struct Simple: View {
    var body: some View {
        Text("A")
    }
}

struct Wrapped: View {
    var body: some View {
        Text("B").padding()
    }
}

#Preview {
    VStack {
        Simple()
        Wrapped()
    }
}
Check yourself

Why are SwiftUI views structs rather than classes?

Build a stat tile

Build the view

Build a StatTile view showing a large value above a small caption, centred, with padding and a grey background with rounded corners. The preview must show two of them side by side.

Solution unlocks after 3 attempts

Compose two custom views

Build the view

Make a Card view containing a title and a Badge, then show two cards stacked vertically.

Solution unlocks after 3 attempts
Explain itSaved on this device. Never graded.

Describe, in your own words, one bug that is possible in UIKit and structurally impossible in SwiftUI. Then describe one thing UIKit lets you do that SwiftUI makes harder.

Checkpoint

You can now:

  • Write a View and explain every part of the declaration
  • Say what some View buys you over View
  • Explain why body must be pure and how declarative differs from imperative
  • Compose custom views and pass data down through initializers

Next up: the layout system — how SwiftUI actually decides where things go, which is not what most people assume.

How well do you know this now? Rating yourself honestly, then being tested on it, is how you find out where your intuition is wrong.