
Swiftui Performance Audit
- 8 installs
- 33 repo stars
- Updated April 26, 2026
- bighardperson/computer-science-skills-collection
SwiftUI Performance Audit is a Claude Code skill that audits SwiftUI runtime performance from code review and Instruments profiling to fix slow rendering, janky scrolling, and excessive view updates.
About
SwiftUI Performance Audit is a Claude Code skill that reviews SwiftUI code to diagnose slow rendering, janky scrolling, and excessive view updates. It looks for view invalidation storms, unstable list identity, heavy work in body, and layout thrash, then proposes targeted fixes and, when code review is inconclusive, guides the user through Instruments profiling. A developer uses it when a SwiftUI screen scrolls poorly or burns CPU and they need to find the cause.
- Audits SwiftUI runtime performance from code review and architecture
- Targets view invalidation storms, unstable list identity, heavy work in body, and layout thrash
- Guides Instruments profiling (SwiftUI template + Time Profiler) when code review is inconclusive
Swiftui Performance Audit by the numbers
- 8 all-time installs (skills.sh)
- Ranked #797 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
swiftui-performance-audit capabilities & compatibility
- Capabilities
- code review · debugging · refactoring
- Use cases
- code review · debugging · refactoring · frontend
- Platforms
- macOS
- Pricing
- Free
What swiftui-performance-audit says it does
Audit and improve SwiftUI runtime performance from code review and architecture.
Audit SwiftUI view performance end-to-end, from instrumentation and baselining to root-cause analysis and concrete remediation steps.
Use the SwiftUI template in Instruments (Release build).
npx skills add https://github.com/bighardperson/computer-science-skills-collection --skill swiftui-performance-auditAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 33 |
| Last updated | April 26, 2026 |
| Repository | bighardperson/computer-science-skills-collection ↗ |
What it does
Diagnose and fix slow rendering, janky scrolling, or high CPU in a SwiftUI app.
Who is it for?
Finding the root cause of SwiftUI jank, over-rendering, or high CPU and getting concrete refactors.
Skip if: Non-SwiftUI UI frameworks or general app logic bugs unrelated to view performance.
When should I use this skill?
A SwiftUI screen renders slowly, scrolls with jank, or shows excessive view updates.
What you get
A ranked list of likely root causes with code references and targeted fixes, plus before/after metrics.
- ranked root-cause list with code references
- targeted fix recommendations
- before/after metrics table
By the numbers
- 6 prioritized SwiftUI culprit categories
- 4-stage workflow (review, profile, diagnose, remediate)
Files
SwiftUI Performance Audit
_Attribution: copied from @Dimillian’s Dimillian/Skills (2025-12-31)._
Overview
Audit SwiftUI view performance end-to-end, from instrumentation and baselining to root-cause analysis and concrete remediation steps.
Workflow Decision Tree
- If the user provides code, start with "Code-First Review."
- If the user only describes symptoms, ask for minimal code/context, then do "Code-First Review."
- If code review is inconclusive, go to "Guide the User to Profile" and ask for a trace or screenshots.
1. Code-First Review
Collect:
- Target view/feature code.
- Data flow: state, environment, observable models.
- Symptoms and reproduction steps.
Focus on:
- View invalidation storms from broad state changes.
- Unstable identity in lists (
idchurn,UUID()per render). - Heavy work in
body(formatting, sorting, image decoding). - Layout thrash (deep stacks,
GeometryReader, preference chains). - Large images without downsampling or resizing.
- Over-animated hierarchies (implicit animations on large trees).
Provide:
- Likely root causes with code references.
- Suggested fixes and refactors.
- If needed, a minimal repro or instrumentation suggestion.
2. Guide the User to Profile
Explain how to collect data with Instruments:
- Use the SwiftUI template in Instruments (Release build).
- Reproduce the exact interaction (scroll, navigation, animation).
- Capture SwiftUI timeline and Time Profiler.
- Export or screenshot the relevant lanes and the call tree.
Ask for:
- Trace export or screenshots of SwiftUI lanes + Time Profiler call tree.
- Device/OS/build configuration.
3. Analyze and Diagnose
Prioritize likely SwiftUI culprits:
- View invalidation storms from broad state changes.
- Unstable identity in lists (
idchurn,UUID()per render). - Heavy work in
body(formatting, sorting, image decoding). - Layout thrash (deep stacks,
GeometryReader, preference chains). - Large images without downsampling or resizing.
- Over-animated hierarchies (implicit animations on large trees).
Summarize findings with evidence from traces/logs.
4. Remediate
Apply targeted fixes:
- Narrow state scope (
@State/@Observablecloser to leaf views). - Stabilize identities for
ForEachand lists. - Move heavy work out of
body(precompute, cache,@State). - Use
equatable()or value wrappers for expensive subtrees. - Downsample images before rendering.
- Reduce layout complexity or use fixed sizing where possible.
Common Code Smells (and Fixes)
Look for these patterns during code review.
Expensive formatters in body
var body: some View {
let number = NumberFormatter() // slow allocation
let measure = MeasurementFormatter() // slow allocation
Text(measure.string(from: .init(value: meters, unit: .meters)))
}Prefer cached formatters in a model or a dedicated helper:
final class DistanceFormatter {
static let shared = DistanceFormatter()
let number = NumberFormatter()
let measure = MeasurementFormatter()
}Computed properties that do heavy work
var filtered: [Item] {
items.filter { $0.isEnabled } // runs on every body eval
}Prefer precompute or cache on change:
@State private var filtered: [Item] = []
// update filtered when inputs changeSorting/filtering in body or ForEach
List {
ForEach(items.sorted(by: sortRule)) { item in
Row(item)
}
}Prefer sort once before view updates:
let sortedItems = items.sorted(by: sortRule)Inline filtering in ForEach
ForEach(items.filter { $0.isEnabled }) { item in
Row(item)
}Prefer a prefiltered collection with stable identity.
Unstable identity
ForEach(items, id: \.self) { item in
Row(item)
}Avoid id: \.self for non-stable values; use a stable ID.
Image decoding on the main thread
Image(uiImage: UIImage(data: data)!)Prefer decode/downsample off the main thread and store the result.
Broad dependencies in observable models
@Observable class Model {
var items: [Item] = []
}
var body: some View {
Row(isFavorite: model.items.contains(item))
}Prefer granular view models or per-item state to reduce update fan-out.
5. Verify
Ask the user to re-run the same capture and compare with baseline metrics. Summarize the delta (CPU, frame drops, memory peak) if provided.
Outputs
Provide:
- A short metrics table (before/after if available).
- Top issues (ordered by impact).
- Proposed fixes with estimated effort.
References
Add Apple documentation and WWDC resources under references/ as they are supplied by the user.
- Optimizing SwiftUI performance with Instruments:
references/optimizing-swiftui-performance-instruments.md - Understanding and improving SwiftUI performance:
references/understanding-improving-swiftui-performance.md - Understanding hangs in your app:
references/understanding-hangs-in-your-app.md - Demystify SwiftUI performance (WWDC23):
references/demystify-swiftui-performance-wwdc23.md
{
"ownerId": "kn70pywhg0fyz996kpa8xj89s57yhv26",
"slug": "swiftui-performance-audit",
"version": "1.0.0",
"publishedAt": 1767652022583
}{
"slug": "swiftui-performance-audit",
"name": "Swiftui Performance Audit",
"version": "1.0.0",
"installedAt": 1776152377557,
"source": "skillhub"
}Demystify SwiftUI Performance (WWDC23) (Summary)
Context: WWDC23 session on building a mental model for SwiftUI performance and triaging hangs/hitches.
Performance loop
- Measure -> Identify -> Optimize -> Re-measure.
- Focus on concrete symptoms (slow navigation, broken animations, spinning cursor).
Dependencies and updates
- Views form a dependency graph; dynamic properties are a frequent source of updates.
- Use
Self._printChanges()in debug only to inspect extra dependencies. - Eliminate unnecessary dependencies by extracting views or narrowing state.
- Consider
@Observablefor more granular property tracking.
Common causes of slow updates
- Expensive view bodies (string interpolation, filtering, formatting).
- Dynamic property instantiation and state initialization in
body. - Slow identity resolution in lists/tables.
- Hidden work: bundle lookups, heap allocations, repeated string construction.
Avoid slow initialization in view bodies
- Don’t create heavy models synchronously in view bodies.
- Use
.taskto fetch async data and keepinitlightweight.
Lists and tables identity rules
- Stable identity is critical for performance and animation.
- Ensure a constant number of views per element in
ForEach. - Avoid inline filtering in
ForEach; pre-filter and cache collections. - Avoid
AnyViewin list rows; it hides identity and increases cost. - Flatten nested
ForEachwhen possible to reduce overhead.
Table specifics
TableRowresolves to a single row; row count must be constant.- Prefer the streamlined
Tableinitializer to enforce constant rows. - Use explicit IDs for back deployment when needed.
Debugging aids
- Use Instruments for hangs and hitches.
- Use
_printChangesto validate dependency assumptions during debug.
Optimizing SwiftUI Performance with Instruments (Summary)
Context: WWDC session introducing the next-generation SwiftUI Instrument in Instruments 26 and how to diagnose SwiftUI-specific bottlenecks.
Key takeaways
- Profile SwiftUI issues with the SwiftUI template (SwiftUI instrument + Time Profiler + Hangs/Hitches).
- Long view body updates are a common bottleneck; use "Long View Body Updates" to identify slow bodies.
- Set inspection range on a long update and correlate with Time Profiler to find expensive frames.
- Keep work out of
body: move formatting, sorting, image decoding, and other expensive work into cached or precomputed paths. - Use Cause & Effect Graph to diagnose why updates occur; SwiftUI is declarative, so backtraces are often unhelpful.
- Avoid broad dependencies that trigger many updates (e.g.,
@Observablearrays or global environment reads). - Prefer granular view models and scoped state so only the affected view updates.
- Environment values update checks still cost time; avoid placing fast-changing values (timers, geometry) in environment.
- Profile early and often during feature development to catch regressions.
Suggested workflow (condensed)
1. Record a trace in Release mode using the SwiftUI template. 2. Inspect "Long View Body Updates" and "Other Long Updates." 3. Zoom into a long update, then inspect Time Profiler for hot frames. 4. Fix slow body work by moving heavy logic into precomputed/cache paths. 5. Use Cause & Effect Graph to identify unintended update fan-out. 6. Re-record and compare the update counts and hitch frequency.
Example patterns from the session
- Caching formatted distance strings in a location manager instead of computing in
body. - Replacing a dependency on a global favorites array with per-item view models to reduce update fan-out.
Understanding Hangs in Your App (Summary)
Context: Apple guidance on identifying hangs caused by long-running main-thread work and understanding the main run loop.
Key concepts
- A hang is a noticeable delay in a discrete interaction (typically >100 ms).
- Hangs almost always come from long-running work on the main thread.
- The main run loop processes UI events, timers, and main-queue work sequentially.
Main-thread work stages
- Event delivery to the correct view/handler.
- Your code: state updates, data fetch, UI changes.
- Core Animation commit to the render server.
Why the main run loop matters
- Only the main thread can update UI safely.
- The run loop is the foundation that executes main-queue work.
- If the run loop is busy, it can’t handle new events; this causes hangs.
Diagnosing hangs
- Observe the main run loop’s busy periods: healthy loops sleep most of the time.
- Hang detection typically flags busy periods >250 ms.
- The Hangs instrument can be configured to lower thresholds.
Practical takeaways
- Keep main-thread work short; offload heavy work from event handlers.
- Avoid long-running tasks on the main dispatch queue or main actor.
- Use run loop behavior as a proxy for user-perceived responsiveness.
Understanding and Improving SwiftUI Performance (Summary)
Context: Apple guidance on diagnosing SwiftUI performance with Instruments and applying design patterns to reduce long or frequent updates.
Core concepts
- SwiftUI is declarative; view updates are driven by state, environment, and observable data dependencies.
- View bodies must compute quickly to meet frame deadlines; slow or frequent updates lead to hitches.
- Instruments is the primary tool to find long-running updates and excessive update frequency.
Instruments workflow
1. Profile via Product > Profile. 2. Choose the SwiftUI template and record. 3. Exercise the target interaction. 4. Stop recording and inspect the SwiftUI track + Time Profiler.
SwiftUI timeline lanes
- Update Groups: overview of time SwiftUI spends calculating updates.
- Long View Body Updates: orange >500us, red >1000us.
- Long Platform View Updates: AppKit/UIKit hosting in SwiftUI.
- Other Long Updates: geometry/text/layout and other SwiftUI work.
- Hitches: frame misses where UI wasn’t ready in time.
Diagnose long view body updates
- Expand the SwiftUI track; inspect module-specific subtracks.
- Set Inspection Range and correlate with Time Profiler.
- Use call tree or flame graph to identify expensive frames.
- Repeat the update to gather enough samples for analysis.
- Filter to a specific update (Show Calls Made by
MySwiftUIView.body).
Diagnose frequent updates
- Use Update Groups to find long active groups without long updates.
- Set inspection range on the group and analyze update counts.
- Use Cause graph ("Show Causes") to see what triggers updates.
- Compare causes with expected data flow; prioritize the highest-frequency causes.
Remediation patterns
- Move expensive work out of
bodyand cache results. - Use
Observable()macro to scope dependencies to properties actually read. - Avoid broad dependencies that fan out updates to many views.
- Reduce layout churn; isolate state-dependent subtrees from layout readers.
- Avoid storing closures that capture parent state; precompute child views.
- Gate frequent updates (e.g., geometry changes) by thresholds.
Verification
- Re-record after changes to confirm reduced update counts and fewer hitches.
Related skills
FAQ
What does it look for in code?
View invalidation storms, unstable list identity (id churn / UUID() per render), heavy work in body, layout thrash, and unoptimized images.
What if code review is inconclusive?
It guides you to profile with the Instruments SwiftUI template and Time Profiler on a Release build and share the trace.