Skip to content

Build1 publisher3 min readPublished

Splitting a SwiftUI body into computed properties tidies the file, not the view tree

A dev.to post pins down a refactor most teams mislabel: only a separate View type adds a node with its own identity and dependencies. Computed properties move text around.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened

  • A dev.to post headlined "Breaking Up Big SwiftUI Views the Right Way" covers why computed properties help readability, what @ViewBuilder does, and when a separate View type matters.
  • A computed property does not create a new View type, a new identity, or an independent dependency-tracking node; it is still part of the same enclosing view's body evaluation.
  • The stated distinction: computed properties organize your source code, while separate View types can organize your view hierarchy, dependencies, state and update work.
  • The post frames SwiftUI around identity (recognizing something as the same or different across updates), lifetime (associating state with identity) and dependencies (which data a view reads), and says Apple describes these concepts as fundamental to how SwiftUI decides what needs to change and when.
  • In a UserNameView whose body renders Text(user.name), the Observation system establishes a dependency on user.name because the body reads that property; if user.email changes but body does not read email, that change does not create the same dependency.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

A post on dev.to titled "Breaking Up Big SwiftUI Views the Right Way" draws the line most refactors blur: computed properties organize source code, while separate `View` types can organize the view hierarchy, dependencies, state and update work [1][3]. That matters because the standard cure for the four-hundred-line body nobody wants to open, splitting it into header, content and footer, tends to get described in review as a performance change when it is a readability change [13][2].

The mechanical claim is narrow and checkable. A computed property does not create a new `View` type, a new identity, or an independent dependency-tracking node; it stays part of the enclosing view's `body` evaluation [2]. The `some View` return type does not change that, because the compiler does not promote a property into an independent SwiftUI view node just because it returns an opaque view [8]. So the header/content/footer split adds three symbols to the file and zero nodes to the hierarchy [17].

The article's worked example is the useful part. A `DashboardView` holds `@State private var count` alongside a private `expensiveSection` property that builds a `ForEach(0..<200)` of rows, and when the body is evaluated again, evaluating the `VStack` also evaluates the expression that produces `expensiveSection` [7]. Move the same rows into `private struct ExpensiveSection: View` and SwiftUI gets another node with its own identity and dependencies; if that section does not depend on `count`, changing `count` does not by itself mean the section's body must be updated [9][10].

The author is careful about the ceiling. A separate `View` type does not make SwiftUI faster; it gives you somewhere to establish narrower dependencies and isolate state and work [11]. Use computed properties where they make a view easier to read, and stop treating them as performance boundaries [12].

The framing worth keeping is the dependency one. Apple treats identity, lifetime and dependencies as fundamental to how SwiftUI decides what needs to change and when [4], and according to the post, Apple's documentation says Observation tracks the observable properties actually read during a view's `body` evaluation [6]. A view whose body reads `user.name` depends on `user.name`; a change to `user.email` that the body never reads does not create the same dependency [5]. The question is what data a view depends on, not which large view happens to contain the code [15].

What to watch is the case the post sets up just as the supplied text stops: an `@Observable DashboardModel` carrying `sessions`, `currentStreak`, `isSyncing`, `errorMessage` and `selectedWorkout`, passed whole into a `SummarySection` [14][16]. By the tracking rule above, a subview handed the entire model but reading only `currentStreak` in its body establishes a dependency only on `currentStreak` [18], so the isolation comes from what the body reads rather than from the parameter list. Before extracting anything, check whether the section you are isolating reads state that changes often. If it does, the new type gives you a node without giving you a narrower dependency [10][11].

Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories