| name | swiftui-ui-patterns |
| description | Best practices and example-driven guidance for building SwiftUI views and components. Use when creating or refactoring SwiftUI UI, designing tab architecture with TabView, composing screens, or needing component-specific patterns and examples. |
SwiftUI UI Patterns
Quick start
Choose a track based on your goal:
Existing project
- Identify the feature or screen and the primary interaction model (list, detail, editor, settings, tabbed).
- Find a nearby example in the repo with
rg "TabView\(" or similar, then read the closest SwiftUI view.
- Apply local conventions: prefer SwiftUI-native state, keep state local when possible, and use environment injection for shared dependencies.
- Choose the relevant component reference from
references/components-index.md and follow its guidance.
- Build the view with small, focused subviews and SwiftUI-native data flow.
New project scaffolding
- Start with
references/app-scaffolding-wiring.md to wire TabView + NavigationStack + sheets.
- Add a minimal
AppTab and RouterPath based on the provided skeletons.
- Choose the next component reference based on the UI you need first (TabView, NavigationStack, Sheets).
- Expand the route and sheet enums as new screens are added.
General rules to follow
- Use modern SwiftUI state (
@State, @Binding, @Observable, @Environment) and avoid unnecessary view models.
- Prefer composition; keep views small and focused.
- Use async/await with
.task and explicit loading/error states.
- Maintain existing legacy patterns only when editing legacy files.
- Follow the project's formatter and style guide.
- Sheets: Prefer
.sheet(item:) over .sheet(isPresented:) when state represents a selected model. Avoid if let inside a sheet body. Sheets should own their actions and call dismiss() internally instead of forwarding onCancel/onConfirm closures.
Workflow for a new SwiftUI view
- Define the view's state and its ownership location.
- Identify dependencies to inject via
@Environment.
- Sketch the view hierarchy and extract repeated parts into subviews.
- Implement async loading with
.task and explicit state enum if needed.
- Add accessibility labels or identifiers when the UI is interactive.
- Validate with a build and update usage callsites if needed.
Component references
Use references/components-index.md as the entry point. Each component reference should include:
- Intent and best-fit scenarios.
- Minimal usage pattern with local conventions.
- Pitfalls and performance notes.
- Paths to existing examples in the current repo.
Sheet patterns
Item-driven sheet (preferred)
@State private var selectedItem: Item?
.sheet(item: $selectedItem) { item in
EditItemSheet(item: item)
}
Sheet owns its actions
struct EditItemSheet: View {
@Environment(\.dismiss) private var dismiss
@Environment(Store.self) private var store
let item: Item
@State private var isSaving = false
var body: some View {
VStack {
Button(isSaving ? "Saving…" : "Save") {
Task { await save() }
}
}
}
private func save() async {
isSaving = true
await store.save(item)
dismiss()
}
}
Adding a new component reference
- Create
references/<component>.md.
- Keep it short and actionable; link to concrete files in the current repo.
- Update
references/components-index.md with the new entry.