SwiftUI Debugging
Diagnostic decision trees for SwiftUI views that don't update, previews that crash, layout problems, and view identity. The skill works through each symptom systematically so you stop guessing under pressure.
When to Use
Use this skill when:
- A view isn't updating when you expect it to
- A preview crashes or won't load
- Layout looks wrong on specific devices or screen sizes
- State resets unexpectedly when you navigate or toggle a condition
- You're tempted to add
@ObservedObjecteverywhere as a band-aid
Core principle: Start with observable symptoms, test systematically, and eliminate causes one by one. Don't guess.
Example Prompts
Questions you can ask your assistant that will draw from this skill:
- "My list item doesn't update when I tap the favorite button, even though the data changed."
- "Preview crashes with 'No ObservableObject of type AppModel found' but the app runs fine."
- "My counter resets to 0 every time I toggle a boolean. Why?"
- "I'm using
@Observablebut the view still doesn't update when I change the property." - "My text field loses focus when I start typing."
- "My sheet always skips its loading animation and shows the completed state."
- "Layout looks wrong on iPad but fine on iPhone."
What This Skill Provides
View Not Updating Decision Tree
- Struct mutation – mutating a local copy instead of the
@Stateproperty itself, so SwiftUI never sees the change - Lost binding identity – a
.constant()binding discards every write, so the child's edits never reach the parent - Accidental view recreation – conditionals destroy and recreate views, resetting
@State - Missing observer – an object SwiftUI can't watch, and when to use
@Stateor a plain property with@Observable @ViewBuilderclosure re-initialization – a sheet or modal child skips its loading state because a parent state change re-evaluates the closure
Preview Crashes Decision Tree
- Names that don't resolve –
cannot find 'X' in scopeis a build error, so the preview never runs - Missing dependencies – the preview runs, then dies because nothing supplied an environment value or object
- State initialization failures – array bounds, forced unwraps, and invalid defaults
- Cache corruption – the code builds but the preview fails, fixed in four ordered steps
Layout Issues Quick Reference
ZStackordering andzIndexGeometryReadersizing- Safe area handling
- Modifier order (each modifier wraps the view above it)
frame()vsfixedSize()
View Identity
- Structural vs explicit identity
- When
.id()helps and when it hurts - Preserving state with
.opacity()instead of a conditional - Stable
ForEachIDs, so text fields keep focus
Tools and Verification
Self._printChanges()from code or LLDB, how to read its output, and why it must not ship (it still prints in Release builds)- Xcode's Debug View Hierarchy for layout inspection
- Confirming a fix in the simulator, naming the device explicitly so a second booted simulator can't hand you the wrong screenshot
- Pressure scenarios for when a deadline tempts you to guess
Key Pattern
Diagnosing a View That Isn't Updating
swift
// Use Self._printChanges() to understand why body was called
var body: some View {
let _ = Self._printChanges() // Debug only — still prints in Release, so remove before shipping
Text("Hello")
}
// Output such as "MyView: __count changed." names the property that triggered the updateDocumentation Scope
This page documents the debugging skill (in the axiom-swiftui suite), which your assistant draws on when a SwiftUI view won't update, a preview crashes, or layout goes wrong.
- For complex cases the decision trees don't resolve, see swiftui-debugging-diag
- For building previews rather than fixing crashes (slow previews,
@Previewable,PreviewModifier), see swiftui-previews
Related
- swiftui-performance – Profiling with the SwiftUI Instrument when updates are slow rather than missing
- xcode-debugging – Environment-first diagnostics when the app crashes at runtime or the build fails
- swift-concurrency –
@MainActor, async/await, and data race warnings - lldb – LLDB workflows beyond
Self._printChanges() - deep-link-debugging – Debug deep links that jump the simulator straight to the problem screen
- /axiom:test-simulator – Multi-step simulator verification after a fix
Resources
WWDC: 2021-10022, 2023-10149, 2023-10160, 2025-256, 2025-306
Docs: /swiftui/managing-model-data-in-your-app, /observation