name: swiftui-performance-audit description: Use when diagnosing SwiftUI jank, slow rendering, high CPU/memory, identity churn, layout thrash, or update storms.
SwiftUI Performance Audit
Diagnose SwiftUI performance from code first, then ask for runtime profiling evidence when code review cannot explain the symptom.
Responsibility
Owns: SwiftUI invalidation, identity, body cost, layout cost, image rendering, broad observation, update storms, and profiling guidance.
Does NOT own: General app hangs outside SwiftUI, networking performance, persistence query design beyond UI-facing fetch cost, or speculative optimization without symptoms.
Intake
Collect the smallest useful packet:
- Target view or feature.
- Symptom: slow render, janky scroll, high CPU, memory growth, hang, or excessive updates.
- Reproduction steps.
- Device/simulator, OS version, Debug/Release, and data volume.
- Any profiling evidence: Instruments trace, screenshots, logs, or before/after metrics.
If code is available, start with code review. If not, ask for the target view and data-flow slice.
Code-First Review
Check these first:
- Broad observation: list rows or child views read a large
@Observable, environment model, or store when they only need a few values. - Unstable identity:
ForEachover indices,id: \.selffor mutable values, orUUID()allocated during rendering. - Heavy body work: sorting, filtering, grouping, date/number formatter creation, image decoding, I/O, or allocation in
body. - Layout thrash: nested
GeometryReader, preference-key feedback loops, deep layout hierarchies, or frequent geometry state writes. - Image cost: large images rendered without downsampling or decoded on the main thread.
- Animation cost: broad animations or transitions applied to large subtrees.
- Excess state writes:
onChange, scroll handlers, timers, or publishers assigning equivalent values repeatedly.
Use swiftui-patterns/references/performance.md for detailed examples and remediation patterns.
Profiling Guidance
Ask for runtime evidence when:
- Code review finds several plausible causes but no clear winner.
- The issue only appears on device, with real data, or in Release.
- The user asks for proof instead of code-level suspicion.
Prefer:
- Instruments SwiftUI template for body/update cost and invalidation.
- Time Profiler for CPU-heavy work.
- Allocations/Memory Graph for memory growth.
- Hangs diagnostics when the UI becomes unresponsive.
Tell the user to profile the same interaction before and after fixes. Without comparable captures, do not claim performance improved.
Remediation Priority
- Narrow dependencies and observation fan-out.
- Stabilize identity.
- Move heavy work out of
body. - Downsample or cache image work outside the render path.
- Simplify layout and geometry feedback.
- Reduce redundant state writes.
- Use
equatable()only when equality is cheaper than recomputing the subtree and inputs are truly value-semantic.
Output Shape
## Performance Audit
| Issue | Evidence | Impact | Fix |
## Likely Root Cause
[Most likely cause and why]
## Validation
[How to prove the fix worked]
## Confidence
[High / Medium / Low] - [code-backed, trace-backed, or assumption-backed]
Rules
- Do not optimize code with no symptom, hot path, or evidence.
- Do not treat simulator performance as representative of device performance.
- Do not cache derived state in
@Statewithout explicit invalidation logic. - Do not add
EquatableViewor.equatable()before fixing broad observation and unstable identity. - Report uncertainty directly when profiling evidence is missing.