Design SwiftUI view hierarchies and state management for VitalArc features. Use when planning new screens, complex UI components, or navigation flows...
Designs SwiftUI view hierarchies, state management, and navigation for VitalArc features.
Execution: Runs in forked context with Plan agent for isolated analysis.
Auto-invoke when:
VitalArc/Modules/
āāā Workout/Presentation/ # Exercises, templates, logging
āāā Wellness/Presentation/ # Health dashboard, metrics, sleep
āāā Shared/
āāā DesignSystem/ # Tokens and reusable components
āāā Common/ # Cross-feature components
āāā User/Presentation/ # Onboarding, settings, profile
āāā Analytics/Presentation/ # Charts, insights
View + ViewModel:
// ViewModel (@Observable, @MainActor)
@MainActor
@Observable
final class FeatureViewModel {
var items: [Item] = []
var isLoading = false
var error: Error?
private let useCase: SomeUseCase
init(useCase: SomeUseCase) {
self.useCase = useCase
}
func loadItems() async {
isLoading = true
defer { isLoading = false }
do {
items = try await useCase.execute()
} catch {
self.error = error
}
}
}
// View
struct FeatureView: View {
@State private var viewModel: FeatureViewModel
init(useCase: SomeUseCase) {
_viewModel = State(initialValue: FeatureViewModel(useCase: useCase))
}
var body: some View {
// ...
}
}
Design System Usage:
var body: some View {
VStack(spacing: Spacing.md) {
Text("Title")
.font(.vitalH1)
.foregroundStyle(Color.vitalAdaptiveTextPrimary)
VitalCard {
// Content
}
VitalButton("Action", style: .primary) {
// Action
}
}
.padding(Spacing.screenPadding)
.background(Color.vitalAdaptiveBackground)
}
# Find similar views
ls VitalArc/Modules/[Domain]/Presentation/
# Check design system components
ls VitalArc/Modules/Shared/DesignSystem/Components/
# Find ViewModels
grep -r "final class.*ViewModel" VitalArc/Modules/
Break down into:
ViewModel responsibilities:
@Observable properties)View responsibilities:
Always use tokens:
| Instead of | Use |
|---|---|
Color.blue |
Color.vitalPrimary |
Color.red |
Color.vitalDanger |
.padding(16) |
.padding(Spacing.md) |
.font(.title) |
.font(.vitalH1) |
| Custom card | VitalCard { } |
| Custom button | VitalButton() |
## UI Design: [Feature Name]
### Navigation
- **Entry point**: [Tab / Button / Link]
- **Presentation**: [Push / Sheet / FullScreenCover]
- **Back navigation**: [Standard / Custom]
### View Hierarchy
FeatureView (Screen) āāā FeatureViewModel āāā HeaderSection ā āāā TitleView ā āāā ActionButtons āāā ContentSection ā āāā List/ScrollView ā āāā ItemRowView (repeated) āāā EmptyStateView (conditional) āāā LoadingOverlay (conditional)
### ViewModel
**File**: `VitalArc/Modules/[Domain]/Presentation/[Feature]ViewModel.swift`
```swift
@MainActor
@Observable
final class [Feature]ViewModel {
// State
var items: [Item] = []
var isLoading = false
var selectedItem: Item?
var showingSheet = false
var error: Error?
// Dependencies
private let fetchUseCase: Fetch[Item]UseCase
private let saveUseCase: Save[Item]UseCase
// Actions
func loadItems() async { }
func selectItem(_ item: Item) { }
func saveItem() async { }
}
File: VitalArc/Modules/[Domain]/Presentation/[Feature]View.swift
Owns: [Feature]ViewModel
Shows: [Description of content]
struct [Feature]View: View {
@State private var viewModel: [Feature]ViewModel
var body: some View {
NavigationStack {
content
.navigationTitle("[Title]")
.toolbar { toolbarContent }
}
.task { await viewModel.loadItems() }
}
@ViewBuilder
private var content: some View {
if viewModel.isLoading {
ProgressView()
} else if viewModel.items.isEmpty {
VitalEmptyState(...)
} else {
itemList
}
}
}
File: VitalArc/Modules/[Domain]/Presentation/[Item]RowView.swift
Purpose: Single item in list
struct [Item]RowView: View {
let item: [Item]
let onTap: () -> Void
var body: some View {
VitalCard {
HStack(spacing: Spacing.md) {
// Content
}
}
}
}
VitalCard - Item containersVitalButton - ActionsVitalEmptyState - No content stateColor.vitalPrimary - Accent colorSpacing.md/lg - Layout spacing.font(.vitalBody) - Text styling[Loading] ā [Empty] or [Content]
[Content] ā [Detail Sheet] ā [Content]
[Content] ā [Edit Mode] ā [Content]
[Any] ā [Error Alert] ā [Previous]
## Example: Notifications Settings UI
```markdown
## UI Design: Notification Settings
### Navigation
- **Entry**: Profile Tab ā Settings ā Notifications
- **Presentation**: Push navigation
### View Hierarchy
NotificationSettingsView (Screen) āāā NotificationSettingsViewModel āāā MasterToggleSection ā āāā Toggle (Enable All) āāā ReminderTypesSection ā āāā NotificationTypeRow (Workout) ā āāā NotificationTypeRow (Recovery) ā āāā NotificationTypeRow (Sleep) āāā ScheduleSection ā āāā TimePickerRow āāā PreviewSection āāā NotificationPreviewCard
### ViewModel
```swift
@MainActor
@Observable
final class NotificationSettingsViewModel {
var notificationsEnabled = false
var workoutReminders = true
var recoveryAlerts = true
var sleepReminders = false
var reminderTime = Date()
private let scheduleUseCase: ScheduleNotificationUseCase
func toggleNotifications() async { }
func updateReminderTime(_ time: Date) async { }
}
VitalCard for sectionsToggle with .tint(Color.vitalPrimary)DatePicker for time selectionVitalButton for "Test Notification"