agentleFS
AFK-surfAFK-surf/OpenBridge/.agents/skills/swiftui-view-refactor/SKILL.md

swiftui-view-refactor

Skill430 starsChanged 6 months agoMITView on GitHub

Refactor and review SwiftUI view files for consistent structure, dependency injection, and Observation usage. Use when asked to clean up a SwiftUI view’s layout/ordering, handle view models safely (non-optional when possible), or standardize how dependencies and @Observable state are initialized and passed.

17 other projects use a version of this, 16 of them edited.

Adopted by

Most recently taken up by

Used with

Projects with it also use swiftui-pro.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • kingsidharthSkill

    swiftui-pro

    Comprehensively reviews SwiftUI code for best practices on modern APIs, maintainability, and performance. Use when reading, writing, or reviewing SwiftUI projects.

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

12 rules. 3 are common, 3 are distinctive, and 6 are very specific to this project.

  • 3

    Common

    Most of the 16 edited versions we read keep these.

  • 3

    Distinctive

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Default to MV: Views are lightweight state expressions.

    line 24
  2. Very specific:

    If body grows beyond a screen or has multiple logical sections, split it into smaller subviews.

    line 30
  3. Distinctive:

    Prefer passing small inputs (data, bindings, callbacks) over reusing the entire parent view state.

    line 33
  4. Very specific:

    Avoid patterns where a computed view (or body) returns completely different root branches using if/else.

    line 101
  5. Very specific:

    Prefer a single stable base view, and place conditions inside sections/modifiers (overlay, opacity, disabled, toolbar, row content, etc.).

    line 102
  6. Common:

    Do not introduce a view model unless the request or existing code clearly calls for one.

    line 133
  7. Common:

    If a view model exists, make it non-optional when possible.

    line 134
  8. Very specific:

    Ensure stable view structure: avoid top-level if-based branch swapping.

    line 156
  9. Very specific:

    If a view model exists, replace optional view models with a non-optional @State view model initialized in init by passing dependencies from …

    line 157
  10. Common:

    Keep behavior intact: do not change layout or business logic unless requested.

    line 159
  11. Distinctive:

    Prefer small, explicit helpers over large conditional blocks.

    line 163
  12. Very specific:

    Keep computed view builders below body and non-view computed vars above init.

    line 164

“Common” is measured: kept by at least 6 in 10 of the 16 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

SwiftUI View Refactor

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 (var header: some View { ... }) into dedicated View types when they carry state or complex branching.
  • It's fine to keep related subviews as computed view properties in the same file; extract to a standalone View struct only when it structurally makes sense or when reuse is intended.
  • Prefer passing small inputs (data, bindings, callbacks) over reusing the entire parent view state.

Example (extracting a section):

var body: some View {
    VStack(alignment: .leading, spacing: 16) {
        HeaderSection(title: title, isPinned: isPinned)
        DetailsSection(details: details)
        ActionsSection(onSave: onSave, onCancel: onCancel)
    }
}

Example (long body → shorter body + computed views in the same file):

var body: some View {
    List {
        header
        filters
        results
        footer
    }
}

private var header: some View {
    VStack(alignment: .leading, spacing: 6) {
        Text(title).font(.title2)
        Text(subtitle).font(.subheadline)
    }
}

private var filters: some View {
    ScrollView(.horizontal, showsIndicators: false) {
        HStack {
            ForEach(filterOptions, id: \.self) { option in
                FilterChip(option: option, isSelected: option == selectedFilter)
                    .onTapGesture { selectedFilter = option }
            }
        }
    }
}

Example (extracting a complex computed view):

private var header: some View {
    HeaderSection(title: title, subtitle: subtitle, status: status)
}

private struct HeaderSection: View {
    let title: String
    let subtitle: String?
    let status: Status

    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(title).font(.headline)
            if let subtitle { Text(subtitle).font(.subheadline) }
            StatusBadge(status: status)
        }
    }
}

3b) Keep a stable view tree (avoid top-level conditional view swapping)

  • Avoid patterns where a computed view (or body) returns completely different root branches using if/else.
  • Prefer a single stable base view, and place conditions inside sections/modifiers (overlay, opacity, disabled, toolbar, row content, etc.).
  • Root-level branch swapping can cause identity churn, broader invalidation, and extra recomputation in SwiftUI.

Prefer:

var body: some View {
    List {
        documentsListContent
    }
    .toolbar {
        if canEdit {
            editToolbar
        }
    }
}

Avoid:

var documentsListView: some View {
    if canEdit {
        editableDocumentsList
    } else {
        readOnlyDocumentsList
    }
}

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.
  • Avoid bootstrapIfNeeded patterns.

Example (Observation-based):

@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

  1. Reorder the view to match the ordering rules.
  2. Favor MV: move lightweight orchestration into the view using @State, @Environment, @Query, task, and onChange.
  3. Ensure stable view structure: avoid top-level if-based branch swapping; move conditions to localized sections/modifiers.
  4. If a view model exists, replace optional view models with a non-optional @State view model initialized in init by passing dependencies from the view.
  5. Confirm Observation usage: @State for root @Observable view models, no redundant wrappers.
  6. 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.
  • For MV-first guidance and rationale, see references/mv-patterns.md.

Large-view handling

  • When a SwiftUI view file exceeds ~300 lines, split it using extensions to group related helpers. Move async functions and helper functions into dedicated private extensions, separated with // MARK: - comments that describe their purpose (e.g., // MARK: - Actions, // MARK: - Subviews, // MARK: - Helpers). Keep the main struct focused on stored properties, init, and body, with view-building computed vars also grouped via marks when the file is long.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.