Generate SwiftUI UI Patterns and Best Practices
A skill for building or refactoring SwiftUI screens with correct state ownership, navigation, sheets, and async patterns.
Why it matters
Accelerate SwiftUI development by providing structured patterns and best practices for creating robust and maintainable UI components, screens, and flows.
Outcomes
What it gets done
Generate reusable SwiftUI UI components based on established patterns.
Provide guidance on state management, navigation, and asynchronous operations.
Assist in refactoring existing SwiftUI code to adhere to modern best practices.
Help debug common SwiftUI implementation issues and anti-patterns.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/ag-swiftui-ui-patterns | bash Overview
SwiftUI UI Patterns
A skill for building or refactoring SwiftUI screens with correct state ownership, navigation, sheet, and async-state patterns, following a state-ownership decision table and a build-and-verify workflow. Use it when creating or refactoring SwiftUI screens/components; it defers to legacy ObservableObject patterns only when the deployment target predates the iOS 17 Observation API.
What it does
This skill guides building or refactoring SwiftUI screens, flows, and reusable components with correct navigation, sheet, async-state, preview, and state-ownership patterns. It offers two tracks: for an existing project, identify the feature's interaction model, find a nearby example in the repo, apply local conventions (SwiftUI-native state, environment injection for shared dependencies), and consult the relevant component reference; for new project scaffolding, wire TabView + NavigationStack + sheets from a reference skeleton, add a minimal AppTab and RouterPath, and expand route/sheet enums as screens are added. General rules mandate modern SwiftUI state (@State, @Binding, @Observable, @Environment) while avoiding unnecessary view models, falling back to ObservableObject/@StateObject/@ObservedObject/@EnvironmentObject only when the deployment target is iOS 16 or earlier and can't use the Observation API. It prefers composition with small focused views, async/await via .task with explicit loading/error states, environment injection for shared app services but explicit initializer injection for feature-local dependencies, and always calling out the minimum OS a pattern depends on. For sheets it prefers .sheet(item:) over .sheet(isPresented:) when state represents a selected model, avoids if let inside a sheet body, and has sheets own their dismissal internally rather than forwarding callback closures. For scroll-driven reveals it prefers deriving a single normalized progress value from scroll offset rather than parallel gesture state machines. A state-ownership table maps each scenario to its preferred pattern: local UI state to @State, child-mutates-parent to @Binding, root-owned reference models on iOS 17+ to @State with @Observable, injected @Observable models to explicit stored properties, shared app services to @Environment(Type.self), and legacy iOS 16 reference models to @StateObject/@ObservedObject - always choosing the ownership location first, then the wrapper, and never introducing a reference model when plain value state suffices. It names explicit anti-patterns: giant views mixing layout/business-logic/networking/routing/formatting in one file, multiple boolean flags for mutually exclusive sheets/alerts/destinations, live service calls inside body-driven code paths, reaching for AnyView to paper over type mismatches instead of better composition, and defaulting every dependency to @EnvironmentObject without a clear ownership reason. A six-step new-view workflow covers defining state ownership and minimum OS upfront, separating @Environment dependencies from explicit initializer inputs, sketching the view/routing hierarchy and building to confirm no compiler errors before proceeding, implementing async loading with .task/.task(id:) and explicit states, adding previews and accessibility identifiers, and a final validation pass checking compiler errors, preview crashes, state propagation, and re-render risk from list identity/observation scope. Cross-cutting reference files cover NavigationStack ownership, centralized sheet presentation, deep-link routing, app-shell wiring, async state patterns, preview fixtures, and performance guardrails.
When to use - and when NOT to
Use it when creating or refactoring SwiftUI screens, flows, or reusable UI components, or when guidance is needed on navigation, sheets, async state, previews, or component patterns. It defers to legacy patterns only when editing legacy files, and otherwise prefers the newest SwiftUI API that fits the deployment target.
Inputs and outputs
Inputs: the SwiftUI feature or screen being built/refactored, its primary interaction model, and the app's minimum deployment target.
Outputs: SwiftUI view code with correct state-ownership choices, navigation/sheet wiring, async loading patterns, and previews - validated against a build-and-verify checklist.
// Preferred: sheet driven by a selected model, owning its own dismissal
.sheet(item: $selectedItem) { item in
DetailView(item: item)
}
Who it's for
iOS/macOS developers building or refactoring SwiftUI screens who need consistent, correctly-scoped state ownership, navigation, and sheet patterns rather than ad hoc boolean flags and giant views.
FAQ
Common questions
Discussion
Questions & comments ยท 0
Sign In Sign in to leave a comment.