SwiftUI Expert
Priority: P0 (CRITICAL)
Role: iOS UI Expert. Prioritize smooth 60fps, clean data flow.
Implementation Guidelines
- Views: Small, composable structs. Extract subviews often to keep
bodyclean. - State Selection (iOS 17+, default):
- @State for local simple data (Booleans, Strings, local view toggles).
- @State for VMs marked
@Observable(owning parent creates it once:@State private var vm = MyViewModel()). - Plain
let/property for passed-in@ObservableVMs — no wrapper needed, child views auto-track only the fields they read. - Legacy (pre-iOS 17 or Combine
ObservableObjectVMs): - @StateObject for VMs (initialized only once in parent view).
- @ObservedObject for passed-in VMs (initialized by parent).
- Prefer migrating
ObservableObjectVMs to@Observablewhen the deployment target allows it — see ios-state-management. - Modifiers: Order matters sequentially. Apply layout modifiers before visual ones (e.g.,
.padding().background()). - Preview: Always provide
PreviewProvideror#Previewfor every view.
Verification Checklist (Mandatory)
- [ ] Body Property: body property computationally cheap? (No complex logic or calculations).
- [ ] State Flow:
@State/@StateObjectVM initialized only once (in parent)? - [ ] Identity: Lists/ForEach stable
id? - [ ] Main Actor: UI updates strictly on Main Actor?
Anti-Patterns
- No Logic in Body: Move calculations to ViewModel or computed vars. Keep
bodyfor UI composition only. - No ObservedObject Init: NOT init
@ObservedObjectinside View settings — this causes leaks and performance issues. - No New
ObservableObjectVMs on iOS 17+: Use@Observablefor new code; reserveObservableObject/@Publishedfor code that must support pre-17 or already depends on Combine operators. - No Hardcoded Sizes: Use flexible frames and spacers for responsive UI.
Canonical response anchors
When this skill applies, preserve the following domain terminology or equivalent concrete examples in the answer when relevant:
- @State for local simple data