What is a View, what does some View mean, and why do modifiers wrap rather than mutate?

8.1.1 · L1 · Interview

Predict first

What is the concrete type of body here?

import SwiftUI

struct Mod: View { var body: some View { Text("x").padding().background(Color.red) } }
print(type(of: Mod().body))
  • Text
  • ModifiedContent<ModifiedContent<Text, _PaddingLayout>, _BackgroundStyleModifier<Color>>
  • AnyView

Predict first

What type does a VStack of two views produce?

import SwiftUI

struct Stack: View { var body: some View { VStack { Text("a"); Text("b") } } }
print(type(of: Stack().body))
  • VStack<TupleView<(Text, Text)>>
  • Array<Text>
  • VStack<Text>

Mental model. A view is a blueprint, not a building. Modifiers are extra layers of tracing paper placed over the blueprint; each layer is a new sheet, and the order you stack them changes the result.

A View is a protocol with one requirement: var body: some View. Conforming types are usually small structs, and creating one is cheap: it is a description, not a screen object. SwiftUI calls body to get the description of the next level down, and repeats until it reaches built-in primitives (Text, Color, shapes) that it renders itself. some View is an opaque return type: body returns one specific concrete type, chosen by the compiler, which the caller treats only as “some view”. That keeps the full type information available for optimisation, unlike any View or AnyView, which erase it.

A modifier like .padding() does not change the view it is called on. It returns a new view that wraps it: Text("x").padding().background(.red) has the type ModifiedContent<ModifiedContent<Text, _PaddingLayout>, …>. That is why order matters: padding then background colours the padded area, while background then padding colours only the text. The view builder (@ViewBuilder) turns several statements into a TupleView or, for if and switch, into a conditional wrapper. Because the whole tree’s type is known statically, SwiftUI can diff it cheaply. Keep body pure: it should compute a description from state, not perform side effects.

Each modifier wraps the previous view in another ModifiedContent, so the type records the whole chain. (The private type names can change between SDKs; the nesting is the point.)

The view builder collects the statements into a TupleView, so the child types are all visible to SwiftUI.

Common traps

  • Treating a view struct like a UIKit object with identity. It is recreated freely; durable state lives in @State and models.
  • Putting side effects in body. It can run many times and at any moment; use task, onChange or an action.

They might follow up with

  • Why prefer some View to AnyView? It keeps the concrete type, so SwiftUI can diff statically and avoid type-erasure costs.
  • Why does the order of modifiers matter? Each modifier wraps the result of the one before.

Related questions

  • 8.1.2 What is structural versus explicit identity in SwiftUI, and what happens to state when identity changes?
  • 8.2.1 How does SwiftUI decide to re-run body, and how do you keep updates cheap?
  • 8.5.1 How does SwiftUI’s layout negotiation work, and why do frame and padding order change the result?

What is structural versus explicit identity in SwiftUI, and what… →