Back to Debugging & Troubleshooting

swiftui-performance-audit

SwiftUIiOSperformanceoptimizationdebuggingmobileapp developmentInstruments
3.7k📄 MIT🕒 2026-03-29Source ↗

Install this skill

npx skills add dimillian/skills

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

The SwiftUI Performance Audit skill provides a structured framework for diagnosing and fixing interface lag, frame drops, and inefficient state propagation. It moves beyond guesswork by enforcing a methodology that identifies expensive operations inside body properties, unstable view identities, and excessive state invalidations. The skill guides you through a two-pronged approach: first, performing static code analysis to spot common anti-patterns like heavy formatting or unoptimized filtering, and second, correlating these findings with Xcode Instruments telemetry. By systematically auditing the view graph and state dependencies, it helps move work off the main thread and optimizes complex layout hierarchies. This process ensures that SwiftUI applications maintain responsiveness by reducing unnecessary re-computations and stabilizing the view lifecycle during dynamic data updates.

When to Use This Skill

  • Debugging stutter during scroll interactions in long lists
  • Investigating unexpected UI updates when unrelated data changes
  • Reducing main thread hangs caused by image decoding or heavy data transformation
  • Optimizing complex view hierarchies that result in layout thrashing

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • Why is my SwiftUI list stuttering when scrolling?
  • Identify why this view is re-rendering so frequently
  • Audit this code for SwiftUI performance bottlenecks
  • Help me interpret this Instruments trace for my SwiftUI app
  • How do I prevent my view from recomputing on every state change?

Pro Tips

  • 💡Provide the complete `body` of the suspected slow view and any relevant data sources or state management code for the most accurate review.
  • 💡Clearly describe the exact performance symptoms (e.g., 'lag when scrolling past item 20', 'takes 3 seconds to load view') and precise reproduction steps.
  • 💡If initial code review is inconclusive, be prepared to share screenshots of Instruments traces or specific performance metrics for deeper analysis.

What this skill does

  • Detects excessive re-renders caused by broad observable object updates
  • Identifies performance bottlenecks like inline sorting or object allocation within body
  • Analyzes identity stability in lists and ForEach loops to prevent state reset
  • Provides instructions for capturing and interpreting SwiftUI Instruments traces
  • Suggests refactoring patterns for state management and view decomposition

When not to use it

  • Diagnosing low-level networking or API latency issues
  • Debugging crash-on-launch scenarios unrelated to UI state
  • Resolving purely functional logic bugs that do not impact frame rate

Example workflow

  1. Collect the source code and reproduction steps for the lagging interaction
  2. Perform a code review to locate heavy formatting or unstable identity patterns
  3. Request an Instruments trace if the code review does not resolve the bottleneck
  4. Analyze the SwiftUI view lifecycle events and call tree in the provided trace
  5. Apply targeted optimizations like caching or state refinement and verify results

Prerequisites

  • Xcode project with functional code
  • Access to a Mac with Xcode Instruments installed

Pitfalls & limitations

  • !Prioritizing micro-optimizations before verifying bottlenecks in Instruments
  • !Assuming all performance issues are caused by SwiftUI rather than underlying model logic
  • !Over-using equatable wrappers which can sometimes obscure actual logic errors

FAQ

Why is my list jittery during scrolling?
Jitter is often caused by unstable identities or heavy computations occurring during the scroll event. Check that your ForEach uses unique, stable IDs and avoid logic that filters or sorts arrays directly inside the view body.
What is a view invalidation storm?
This happens when a single state change causes too many views to re-evaluate simultaneously. It is usually fixed by narrowing the scope of your state to only the specific leaf views that need the data.
Do I need to profile every time I fix a performance issue?
Yes, profiling with Instruments provides an empirical baseline. Comparing trace metrics before and after changes is the only way to confirm that your specific fix actually improved real-world performance.

How it compares

Generic prompts often suggest trial-and-error code changes, whereas this skill follows a strict technical audit loop that mirrors Apple's own diagnostic recommendations for SwiftUI.

Source & trust

3.7k stars📄 MIT🕒 Updated 2026-03-29
📄 Full skill instructions — original source: dimillian/skills
# SwiftUI Performance Audit

## 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 (id churn, 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 (id churn, 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/@Observable closer to leaf views).
- Stabilize identities for ForEach and 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 change


### Sorting/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

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/swiftui-performance-audit/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/dimillian/skills/swiftui-performance-audit/SKILL.md
  • Cursor: ~/.cursor/skills/dimillian/skills/swiftui-performance-audit/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/dimillian/skills/swiftui-performance-audit/SKILL.md

🚀 Install with CLI:
npx skills add dimillian/skills

Read the Master Guide: Mastering Agent Skills

Recommended Rules

View more rules

Recommended Workflows

View more workflows

Recommended MCP Servers

View more MCP servers

Take It Further

Maximize your productivity with these powerful resources

📋

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
📖

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid debugging & troubleshooting issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under Debugging & Troubleshooting and is published by Thomas Ricouard, maintained in dimillian/skills.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.