SwiftUI View Refactor
Attribution: Sourced from steipete/agent-scripts by Peter Steinberger. Originally created by @Dimillian from Dimillian/Skills (2025-12-31).
When to Use
- Refactoring SwiftUI views for consistent layout ordering and structure
- Reviewing dependency injection, Observation, and MV patterns
- Extracting subviews and organizing view model relationships
Overview
Apply a consistent structure and dependency pattern to SwiftUI views, with a focus on ordering, Model-View (MV) patterns, careful view model handling, and correct Observation usage.
Core Guidelines
1) View ordering (top → bottom)
- Environment
private/public let
@State / other stored properties
- computed
var (non-view)
init
body
- computed view builders / other view helpers
- helper / async functions
2) Prefer MV (Model-View) patterns
- Default to MV: Views are lightweight state expressions; models/services own business logic.
- Favor
@State, @Environment, @Query, and task/onChange for orchestration.
- Inject services and shared models via
@Environment; keep views small and composable.
- Split large views into subviews rather than introducing a view model.
3) Split large bodies and view properties
- If
body grows beyond a screen or has multiple logical sections, split it into smaller subviews.
- Extract large computed view properties into dedicated
View types when they carry state or complex branching.
- Prefer passing small inputs (data, bindings, callbacks) over reusing the entire parent view state.
// ✅ Extracted sections
var body: some View {
VStack(alignment: .leading, spacing: 16) {
HeaderSection(title: title, isPinned: isPinned)
DetailsSection(details: details)
ActionsSection(onSave: onSave, onCancel: onCancel)
}
}
4) View model handling (only if already present)
- Do not introduce a view model unless the request or existing code clearly calls for one.
- If a view model exists, make it non-optional when possible.
- Pass dependencies to the view via
init, then pass them into the view model in the view's init.
// ✅ Observation-based view model
@State private var viewModel: SomeViewModel
init(dependency: Dependency) {
_viewModel = State(initialValue: SomeViewModel(dependency: dependency))
}
5) Observation usage
- For
@Observable reference types, store them as @State in the root view.
- Pass observables down explicitly as needed; avoid optional state unless required.
Workflow
- Reorder the view to match the ordering rules.
- Favor MV: move lightweight orchestration into the view using
@State, @Environment, @Query, task, and onChange.
- If a view model exists, replace optional view models with a non-optional
@State view model initialized in init.
- Confirm Observation usage:
@State for root @Observable view models, no redundant wrappers.
- Keep behavior intact: do not change layout or business logic unless requested.
Notes
- Prefer small, explicit helpers over large conditional blocks.
- Keep computed view builders below
body and non-view computed vars above init.
1---2name: swiftui-view-refactor3description: SwiftUI view refactor/review: layout ordering, DI, Observation, MV patterns, view models.4---56# SwiftUI View Refactor78> **Attribution:** Sourced from [steipete/agent-scripts](https://github.com/steipete/agent-scripts) by [Peter Steinberger](https://github.com/steipete). Originally created by [@Dimillian](https://github.com/Dimillian) from [Dimillian/Skills](https://github.com/Dimillian/Skills) (2025-12-31).910## When to Use1112- Refactoring SwiftUI views for consistent layout ordering and structure13- Reviewing dependency injection, Observation, and MV patterns14- Extracting subviews and organizing view model relationships1516## Overview1718Apply a consistent structure and dependency pattern to SwiftUI views, with a focus on ordering, Model-View (MV) patterns, careful view model handling, and correct Observation usage.1920## Core Guidelines2122### 1) View ordering (top → bottom)2324- Environment25- `private`/`public` `let`26- `@State` / other stored properties27- computed `var` (non-view)28- `init`29- `body`30- computed view builders / other view helpers31- helper / async functions3233### 2) Prefer MV (Model-View) patterns3435- Default to MV: Views are lightweight state expressions; models/services own business logic.36- Favor `@State`, `@Environment`, `@Query`, and `task`/`onChange` for orchestration.37- Inject services and shared models via `@Environment`; keep views small and composable.38- Split large views into subviews rather than introducing a view model.3940### 3) Split large bodies and view properties4142- If `body` grows beyond a screen or has multiple logical sections, split it into smaller subviews.43- Extract large computed view properties into dedicated `View` types when they carry state or complex branching.44- Prefer passing small inputs (data, bindings, callbacks) over reusing the entire parent view state.4546```swift47// ✅ Extracted sections48var body: some View {49 VStack(alignment: .leading, spacing: 16) {50 HeaderSection(title: title, isPinned: isPinned)51 DetailsSection(details: details)52 ActionsSection(onSave: onSave, onCancel: onCancel)53 }54}55```5657### 4) View model handling (only if already present)5859- Do not introduce a view model unless the request or existing code clearly calls for one.60- If a view model exists, make it non-optional when possible.61- Pass dependencies to the view via `init`, then pass them into the view model in the view's `init`.6263```swift64// ✅ Observation-based view model65@State private var viewModel: SomeViewModel6667init(dependency: Dependency) {68 _viewModel = State(initialValue: SomeViewModel(dependency: dependency))69}70```7172### 5) Observation usage7374- For `@Observable` reference types, store them as `@State` in the root view.75- Pass observables down explicitly as needed; avoid optional state unless required.7677## Workflow78791. Reorder the view to match the ordering rules.802. Favor MV: move lightweight orchestration into the view using `@State`, `@Environment`, `@Query`, `task`, and `onChange`.813. If a view model exists, replace optional view models with a non-optional `@State` view model initialized in `init`.824. Confirm Observation usage: `@State` for root `@Observable` view models, no redundant wrappers.835. Keep behavior intact: do not change layout or business logic unless requested.8485## Notes8687- Prefer small, explicit helpers over large conditional blocks.88- Keep computed view builders below `body` and non-view computed vars above `init`.