If you don’t profile, you’re guessing.
And in SwiftUI, guessing leads to:
- random
.id()hacks - unnecessary
EquatableView - broken animations
- mysterious jank
- premature micro-optimizations
This post is a practical, no-BS guide to profiling SwiftUI apps with Instruments:
- where to look
- what matters
- what to ignore
- how to interpret results
- how to fix real issues
This is how you go from “it feels slow” to “I know why.”
🧠 The Core Principle
Measure first. Optimize second.
SwiftUI performance problems are usually:
- layout thrashing
- excessive body recomputation
- main-thread blocking
- memory churn
- view identity misuse
Instruments shows all of this.
🧰 The 4 Instruments You Actually Need
Ignore the rest. Start with:
- Time Profiler
- SwiftUI
- Allocations
- Leaks
These four cover 95% of SwiftUI issues.
⏱️ 1. Time Profiler — Find the Real Bottleneck
Open:
Xcode → Product → Profile → Time Profiler
Look for:
- long main-thread blocks
- repeated calls to the same functions
- heavy JSON decoding
- image decoding
- layout loops
What to watch:
-
bodybeing called excessively -
ViewGraphupdates - expensive modifiers
- synchronous work in
.task
🧠 Common Time Profiler Patterns
❌ Pattern: Heavy work in body
var body: some View {
let data = heavyComputation()
...
}
Fix:
@State private var data: Data
.task {
data = await heavyComputation()
}
❌ Pattern: Synchronous decoding
let image = UIImage(data: data)
Fix:
- decode off main thread (as covered in #47)
🧬 2. SwiftUI Instrument — View Update Tracing
This is gold.
Enable:
Instruments → SwiftUI
You’ll see:
- which views re-render
- how often
- why
Look for:
- views updating when nothing changed
- large subtrees invalidating
- identity resets
- unexpected animations
🧠 Red Flags in SwiftUI Instrument
- Parent view updates → entire screen redraws
- Small state change → full list invalidates
- Repeated layout passes
- Views recreated instead of updated
These usually mean:
- bad identity
- wrong state placement
- environment misuse
🧪 3. Allocations — Memory Churn
Open:
Instruments → Allocations
Look for:
- rapid object creation
- spikes on scroll
- repeated ViewModel creation
- image churn
- task churn
This catches:
- memory leaks
- over-allocation
- performance killers
🧠 Classic Allocation Problems
❌ Creating objects in body
var body: some View {
let formatter = DateFormatter()
...
}
Fix:
private let formatter = DateFormatter()
Or inject.
❌ Recreating ViewModels
SomeView(viewModel: ViewModel())
Fix:
@StateObject private var vm = ViewModel()
🧯 4. Leaks — Confirm Deallocation
Open:
Instruments → Leaks
Then:
- navigate through your app
- push/pop views
- open/close features
- switch tabs
- open/close windows
Watch:
- ViewModels
- services
- containers
If they don’t deinit — you have a bug.
(See #44 for ownership rules.)
🧭 5. How to Profile a Screen (Step-by-Step)
- Open Instruments
- Select Time Profiler + SwiftUI
- Navigate to the problematic screen
- Interact:
- scroll
- tap
- animate
- load data
- Stop recording
- Inspect:
- main thread
- SwiftUI updates
- allocations
Never profile on simulator. Always use a device.
🧠 6. Interpreting SwiftUI Re-Renders
Ask:
- Which state changed?
- Why did this view invalidate?
- Does identity change?
- Is environment causing this?
- Is a parent invalidating children?
If you can’t answer, your architecture is wrong.
🧬 7. Layout Thrashing Detection
Symptoms:
- high CPU on scroll
- repeated layout passes
- jerky animations
Instruments shows:
- repeated calls to layout functions
-
LayoutComputeractivity
Fixes:
- reduce nested stacks
- avoid GeometryReader abuse
- avoid preference loops
- flatten view hierarchy
🧪 8. Animation Profiling
Use:
- Core Animation
- Time Profiler
Look for:
- dropped frames
- layout during animation
- heavy modifiers during transitions
Common fixes:
- move work out of animation blocks
- avoid layout-changing animations
- precompute values
🧠 9. Real-World Profiling Checklist
Profile:
- cold launch
- scrolling long lists
- navigation push/pop
- tab switching
- deep link entry
- background → foreground
- orientation change
These reveal 90% of issues.
❌ 10. Common Anti-Patterns
Avoid:
- profiling only in debug
- ignoring Instruments warnings
- optimizing without evidence
- blaming SwiftUI blindly
- using random modifiers to “fix” jank
- shipping without profiling
Performance is not optional.
🧠 Mental Model
Think:
User Action
→ State Change
→ View Invalidation
→ Layout
→ Render
→ GPU
Instruments shows you which step is broken.
🚀 Final Thoughts
Instruments turns:
- “it feels slow” into
- “this function blocks the main thread for 32ms” That’s power.
Once you use it regularly:
- architecture improves
- code quality rises
- bugs drop
- confidence grows
Top comments (0)