The body property is the central element of the View protocol in SwiftUI, determining what content is displayed on the screen. According to Apple Developer Documentation, 2024, body is the only required requirement of the View protocol and returns some type that conforms to the same protocol. SwiftUI calls body with every state change to build and compare a new tree of elements.
Key Takeaways
body is a computed property that is the only required requirement of the View protocol. Every structure conforming to View must implement body. The property returns the content that SwiftUI displays on the screen — it can be text, an image, a button, a container with nested elements, or any other type conforming to the View protocol.
The body signature is always fixed: var body: some View { get }. The return type is some View (an opaque type), not a concrete type. This means different Views may return different concrete types in body, but the Swift compiler fixes the concrete type for each implementation at compile time.
According to WWDC 2022, body is the entry point for declarative interface description. Unlike UIKit, where you imperatively create and configure UIView, in SwiftUI you declaratively describe what should be displayed, and SwiftUI itself figures out how to implement it.
body should behave as a pure function — with the same inputs (structure properties and state) it should return the same View tree. If body depends on external mutable state (global variables, UserDefaults without the @AppStorage wrapper), the behavior becomes unpredictable, and SwiftUI may redraw the screen incorrectly.
The computed property body does not store a value — it is computed each time it is accessed. When SwiftUI determines that the state has changed, it recreates the View structure and reads the new body value to get the current element tree for display.
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Counter: \(count)")
.font(.largeTitle)
Button("Increment") {
count += 1
}
.padding()
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
In this example, body returns a VStack containing a Text and a button with modifiers. When the button is pressed, the @State property count increments, SwiftUI recreates the CounterView structure and calls body again to get the updated tree with the new Text value.
Modifiers (.font, .padding, .background, .foregroundColor, .cornerRadius) do not modify the original View but wrap it in ModifiedContent — a new type that adds the modification. Each modifier creates another nesting level, which is important to consider for performance.
some View in the body return type is not just a convention but a compiler requirement. Swift requires that all return paths in body have the same concrete type. Without @ViewBuilder you cannot return Text in one branch and Button in another — the compiler will produce an error.
struct ConditionalView: View {
var isReady: Bool
@ViewBuilder
var body: some View {
if isReady {
Text("Ready")
.foregroundColor(.green)
} else {
ProgressView()
}
}
}
@ViewBuilder on body allows using conditional logic (if/else, switch) without compiler errors. ViewBuilder automatically wraps different branches in ConditionalContent — a special type that hides concrete type differences. This is a key capability for building dynamic interfaces.
Without @ViewBuilder the compiler tries to infer a single type for all return paths. If the types differ — an error occurs. This is why SwiftUI implicitly applies @ViewBuilder to body in View declarations, although in user code you need to explicitly add the annotation for custom methods and properties that return multiple Views.
Using some View instead of a concrete type does not reduce performance — the compiler knows the exact type at compile time and generates direct code without dynamic dispatch. AnyView, on the contrary, uses type erasure with the overhead of wrapping in an existential container.
body is called by SwiftUI in three main scenarios: when the View is first displayed, when @State/@Binding/@ObservedObject/@StateObject changes, and when the parent View passes new values through the initializer. SwiftUI may also call body when environment values (@Environment) change.
The frequency of body calls should not concern you — SwiftUI optimizes redrawing through the identity mechanism. Each View in the hierarchy has a unique identifier. If the identity and input data have not changed — body is not called even if the parent View redraws. This is achieved through Equatable comparison and structural stability.
struct ParentView: View {
var body: some View {
ChildView(name: "Alice") // Stable identity
}
}
struct ChildView: View {
let name: String
var body: some View {
Text("Hello, \(name)!")
}
}
In this example, if ParentView redraws but passes the same name value — ChildView.body is not called. SwiftUI compares the structure's input data and, if unchanged, skips the child component's redraw. This is the view differentiation mechanism.
There are several pitfalls leading to unexpected body calls: using classes without ObservableObject, passing closures created inside body (each closure creation yields a new identity), and incorrect use of EquatableView. If body is called too often — check the identity stability of all child components.
First rule: body should be minimal. Move complex logic into separate computed properties or methods that return View. This improves readability and allows SwiftUI to more accurately determine which parts of the hierarchy have changed. Break large bodies into subcomponents with clear responsibility boundaries.
Second rule: do not use body to perform work. Data loading, network operations, database writes — all of this should happen outside body, in tasks, onChange modifiers, or through ObservableObject. body is intended solely for interface declaration.
Third rule: use the EquatableView property or a custom Equatable protocol for Views if the standard structural comparison is insufficient. This allows you to explicitly tell SwiftUI when a child View needs a redraw and avoid unnecessary body calls.
Fourth rule: if body contains complex computations (formatting, filtering, sorting) — use @State for caching the result or move computations to a separate method called from onChange. Repeated computations in body with every state update is a common cause of animation lag.
Fifth rule: for lists (List, ForEach) ensure stable identifiers via the id parameter. Without stable identity, ForEach recreates all elements on any change, calling body for each of them, even if only one element changed.
Frequently Asked Questions
body is the computed property of the View protocol that returns content for display. It is the only required requirement of the protocol. The return type is some View, which allows SwiftUI to optimize the hierarchy at compile time.
Yes, SwiftUI calls body with every state change (@State, @Binding, @ObservedObject) or input data change. This is normal behavior for a declarative framework. SwiftUI optimizes call frequency through the identity mechanism and Equatable comparison.
some View is an opaque type that hides the concrete implementation. The compiler fixes the type at compile time, ensuring direct call performance. This provides flexibility: you can change the return type without changing the signature.
No, body cannot be optional — the return type some View does not allow nil. If you need to hide an element conditionally, use conditional logic inside @ViewBuilder or return EmptyView, which takes no space in the hierarchy.
Each modifier creates a new ModifiedContent layer, increasing the hierarchy depth. For most screens (up to 50 modifiers) the impact is negligible. Excessive modifier count (hundreds) may slow down diffing. Group related modifiers into custom extensions.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also