| license | Apache-2.0 |
| name | swiftui-data-flow-expert |
| description | SwiftUI data flow expert with @Observable, SwiftData, NavigationStack, and structured concurrency. Activate on: SwiftUI state management, @Observable, SwiftData, NavigationStack, Combine, async/await Swift, structured concurrency, MVVM SwiftUI. NOT for: UIKit legacy (use react-native-architect), Core Data migrations (use ios-core-data-architect), Android Compose (use jetpack-compose-navigation-expert). |
| allowed-tools | Read,Write,Edit,Bash(swift:*,xcodebuild:*) |
| category | Mobile Development |
| tags | ["swiftui","ios","swift","state-management"] |
| pairs-with | [{"skill":"native-app-designer","reason":"Native design patterns inform SwiftUI component architecture"},{"skill":"ios-core-data-architect","reason":"Core Data and SwiftData persistence layer beneath SwiftUI views"}] |
SwiftUI Data Flow Expert
Expert in SwiftUI state management with @Observable, SwiftData persistence, NavigationStack, and structured concurrency patterns for iOS 17+/18.
Decision Points
State Management Choice
1. View-local state (form inputs, toggles, local UI)?
→ Use @State
→ Keep data inside the view
→ Example: @State private var showingSheet = false
2. Shared state across views (user session, app data)?
→ Use @Observable class
→ Pass via @Environment or init
→ Example: @Observable class UserSession
3. Persistent data (saved to disk)?
→ Use SwiftData @Model
→ Access via @Query in views
→ Example: @Model class Expense
4. External data source (network, sensors)?
→ Use @Observable + async methods
→ Wrap in Task {} for concurrency
→ Example: @Observable class WeatherViewModel
Concurrency Model Choice
1. Single async operation (network call)?
→ Use Task {} in SwiftUI .task modifier
→ Handle errors with do/catch
2. Multiple related operations?
→ Use TaskGroup for parallel execution
→ Use await for sequential dependencies
3. Continuous data stream (location, sensors)?
→ Use AsyncStream or AsyncSequence
→ Cancel with .onDisappear
4. Background processing?
→ Use @globalActor or custom actor
→ Never access @MainActor data directly
Data Binding Strategy
1. Direct property binding needed?
→ Use @Bindable(viewModel)
→ Allows $ syntax for Observable objects
2. One-way data flow only?
→ Pass Observable directly
→ Read properties without $
3. Computed property or transformation?
→ Create computed var in Observable class
→ SwiftUI auto-tracks changes
4. Form data with validation?
→ Use @State for working copy
→ Sync to Observable on save/submit
Failure Modes
Symptom: View Won't Re-render
- Detection: Data changes but UI stays stale
- Diagnosis: Missing observation or wrong binding type
- Fix: Ensure @Observable class, check @Bindable usage, verify property access
Symptom: App Crashes on Navigation
- Detection: Fatal error on NavigationLink tap or back navigation
- Diagnosis: Type mismatch in NavigationStack routing
- Fix: Verify navigationDestination(for:) matches NavigationLink value types
Symptom: Data Appears Then Disappears
- Detection: SwiftData @Query shows data briefly, then empty list
- Diagnosis: ModelContext not properly injected or predicate filtering all results