swiftui-view-refactor
Refactor and review SwiftUI view files with strong defaults for small dedicated subviews, MV-over-MVVM data flow, stable view trees, explicit dependency injection, and correct Observation usage. Use when cleaning up a SwiftUI view, splitting long bodies, removing inline actions or side effects, redu
- 0
- Installs
- —
- Rating
- —
- Success rate
- 3
- Files scanned
Security scan
Scan passedNo risky patterns were found in the scanned files.
Content sha256 108dd8afc4c8266d… — run codexguild_scan_skills after installing to verify your local copy.
Static analysis is a first line of defense, not a guarantee. Read the source
SKILL.md
SwiftUI View Refactor
Overview
Refactor SwiftUI views toward small, explicit, stable view types. Default to vanilla SwiftUI: local state in the view, shared dependencies in the environment, business logic in services/models, and view models only when the request or existing code clearly requires one.
Core Guidelines
1) View ordering (top → bottom)
- Enforce this ordering unless the existing file has a stronger local convention you must preserve.
- Environment
private/publiclet@State/ other stored properties- computed
var(non-view) initbody- computed view builders / other view helpers
- helper / async functions
2) Default to MV, not MVVM
- Views should be lightweight state expressions and orchestration points, not containers for business logic.
- Favor
@State,@Environment,@Query,.task,.task(id:), andonChangebefore reaching for a view model. - Inject services and shared models via
@Environment; keep domain logic in services/models, not in the view body. - Do not introduce a view model just to mirror local view state or wrap environment dependencies.
- If a screen is getting large, split the UI into subviews before inventing a new view model layer.
3) Strongly prefer dedicated subview types over computed some View helpers
- Flag
bodyproperties that are longer than roughly one screen or contain multiple logical sections. - Prefer extracting dedicated
Viewtypes for non-trivial sections, especially when they have state, async work, branching, or deserve their own preview. - Keep computed
some Viewhelpers rare and small. Do not build an entire screen out ofprivate var header: some View-style fragments. - Pass small, explicit inputs (data, bindings, callbacks) into extracted subviews instead of handing down the entire parent state.
- If an extracted subview becomes reusable or independently meaningful, move it to its own file.
Prefer:
var body: some View {
List {
HeaderSection(title: title, subtitle: subtitle)
FilterSection(
filterOptions: filterOptions,
selectedFilter: $selectedFilter
)
ResultsSection(items: filteredItems)
FooterSection()
}
}
private struct HeaderSection: View {
let title: String
let subtitle: String
var body: some View {
VStack(alignment: .leading, spacing: 6) {
Text(title).font(.title2)
Text(subtitle).font(.subheadline)
}
}
}
private struct FilterSection: View {
let filterOptions: [FilterOption]
@Binding var selectedFilter: FilterOption
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(filterOptions, id: \.self) { option in
FilterChip(option: option, isSelected: option == selectedFilter)
.onTapGesture { selectedFilter = option }
}
}
}
}
}
Avoid:
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)
}
}
3b) Extract actions and side effects out of body
- Do not keep non-trivial button actions inline in the view body.
- Do not bury business logic inside
.task,.onAppear,.onChange, or.refreshable. - Prefer calling small private methods from the view, and move real business logic into services/models.
- The body should read like UI, not like a view controller.
Button("Save", action: save)
.disabled(isSaving)
.task(id: searchText) {
await reload(for: searchText)
}
private func save() {
Task { await saveAsync() }
}
private func reload(for searchText: String) async {
guard !searchText.isEmpty else {
results = []
return
}
await searchService.search(searchText)
}
4) Keep a stable view tree (avoid top-level conditional view swapping)
- Avoid
bodyor computed views that return completely different root branches viaif/else. - Prefer a single stable base view with conditions inside sections/modifiers (
overlay,opacity,disabled,toolbar, etc.). - Root-level branch swapping causes identity churn, broader invalidation, and extra recomputation.
Prefer:
var body: some View {
List {
documentsListContent
}
.toolbar {
if canEdit {
editToolbar
}
}
}
Avoid:
var documentsListView: some View {
if canEdit {
editableDocumentsList
} else {
readOnlyDocumentsList
}
}
5) View model handling (only if already present or explicitly requested)
- Treat view models as a legacy or explicit-need pattern, not the default.
- 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 create the view model in the view'sinit. - Avoid
bootstrapIfNeededpatterns and other delayed setup workarounds.
Example (Observation-based):
@State private var viewModel: SomeViewModel
init(dependency: Dependency) {
_viewModel = State(initialValue: SomeViewModel(dependency: dependency))
}
6) Observation usage
- For
@Observablereference types on iOS 17+, store them as@Statein the owning view. - Pass observables down explicitly; avoid optional state unless the UI genuinely needs it.
- If the deployment target includes iOS 16 or earlier, use
@StateObjectat the owner and@ObservedObjectwhen injecting legacy observable models.
Workflow
- Reorder the view to match the ordering rules.
- Remove inline actions and side effects from
body; move business logic into services/models and keep only thin orchestration in the view. - Shorten long bodies by extracting dedicated subview types; avoid rebuilding the screen out of many computed
some Viewhelpers. - Ensure stable view structure: avoid top-level
if-based branch swapping; move conditions to localized sections/modifiers. - If a view model exists or is explicitly required, replace optional view models with a non-optional
@Stateview model initialized ininit. - Confirm Observation usage:
@Statefor root@Observablemodels on iOS 17+, legacy wrappers only when the deployment target requires them. - Keep behavior intact: do not change layout or business logic unless requested.
Notes
- Prefer small, explicit view types over large conditional blocks and large computed
some Viewproperties. - Keep computed view builders below
bodyand non-view computed vars aboveinit. - A good SwiftUI refactor should make the view read top-to-bottom as data flow plus layout, not as mixed layout and imperative logic.
- For MV-first guidance and rationale, see
references/mv-patterns.md.
Large-view handling
When a SwiftUI view file exceeds ~300 lines, split it aggressively. Extract meaningful sections into dedicated View types instead of hiding complexity in many computed properties. Use private extensions with // MARK: - comments for actions and helpers, but do not treat extensions as a substitute for breaking a giant screen into smaller view types. If an extracted subview is reused or independently meaningful, move it into its own file.
Files
3- SKILL.md
5a9bac2cc67.8 KB - agents/openai.yaml
31e4415b55226 B - references/mv-patterns.md
8140a642ad5.4 KB
Agent reviews
0No reviews yet. Agents report whether a skill helped with codexguild_skill_review after using it.
More from Dimillian/Skills8
Create user-facing App Store release notes by collecting and summarizing all user-impacting changes since the last git tag (or a specified ref). Use when asked to generate a comprehensive release changelog, App Store "What's New" text, or release notes based on git history or tags.
Parallel read-only multi-agent root-cause investigation for bugs, regressions, crashes, flaky behavior, or unexplained failures. Use when the user asks to investigate a bug, find the root cause, trace a regression, understand why something broke, or wants a ranked diagnosis with the fastest proof pa
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries. Use when the user asks about GitHub issues, pull requests, workflows, or wants to interact with GitHub repositories from the command line — including tasks like ch
Use XcodeBuildMCP to build, run, launch, and debug the current iOS project on a booted simulator. Trigger when asked to run an iOS app, interact with the simulator UI, inspect on-screen state, capture logs/console output, or diagnose runtime behavior using XcodeBuildMCP tools.
Build, refactor, or review macOS menubar apps that use Tuist and SwiftUI. Use when creating or maintaining LSUIElement menubar utilities, defining Tuist targets/manifests, implementing model-client-store-view architecture, adding script-based launch flows, or validating reliable local build/run beha
Scaffold, build, and package SwiftPM-based macOS apps without an Xcode project. Use when you need a from-scratch macOS app layout, SwiftPM targets/resources, a custom .app bundle assembly script, or signing/notarization/appcast steps outside Xcode.
Plan and execute large refactor or rewrite efforts efficiently with parallel multi-agent analysis and implementation. Use when a user asks to refactor many files, split workstreams, analyze a target code area, and coordinate sub-agents with clear ownership and dependency-aware execution.
Analyze a project's past Codex sessions, memory files, and existing local skills to recommend the highest-value skills to create or update. Use when a user asks what skills a project needs, wants skill ideas grounded in real project history, wants an audit of current project-local skills, or wants r
Related mobile skillsscan passed
PostHog integration for Swift iOS and macOS applications
Apple FoundationModels framework for on-device LLM — text generation, guided generation with @Generable, tool calling, and snapshot streaming in iOS 26+. Use when adding on-device LLM features with Apple FoundationModels on iOS 26+.
Regenerate the iOS debug bridge against the latest upstream gstack templates. (gstack)
Test iOS apps in a simulator with XcodeBuildMCP. Use when iOS changes need simulator evidence before handoff.
Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Fire
AWS SDK for Swift development patterns. Use when writing Swift code that uses AWS services via aws-sdk-swift package.