| name | swiftuice-analyze |
| description | This skill should be used when the user asks to find and fix SwiftUI performance issues, optimize SwiftUI views, fix re-render problems, or analyze why their iOS/macOS app UI is slow. It handles the full workflow from code review to trace analysis. |
SwiftUI Performance Analysis
This skill helps find and fix SwiftUI performance issues. When triggered, follow this workflow:
Recommended Workflow
1. Review Code → 2. Record Trace (if needed) → 3. Analyze → 4. Fix
Start with code review (immediate results), then offer trace recording for deeper insights.
When to Use This Skill
Use this skill when user says things like:
- "Find and fix SwiftUI performance issues"
- "My app UI is slow/laggy"
- "Optimize my SwiftUI views"
- "Fix the re-render issues in my app"
- "Why is my view updating so much?"
- "Profile my SwiftUI app"
- "Help with SwiftUI performance"
Mode 1: Code Review (No Trace Required)
Review SwiftUI source files directly for performance anti-patterns. This is the fastest way to find issues.
What to Look For
Scan Swift files for these anti-patterns:
| Pattern | Problem | Fix |
|---|---|---|
@ObservedObject on large objects |
All views re-render on any property change | Use @Observable (iOS 17+) or split state |
| Passing whole model to child views | Unnecessary re-renders when unused properties change | Pass only needed primitives |
| Timer/animation at parent level | Cascades updates to all children | Isolate to affected view only |
Missing Equatable on data-driven views |
Can't skip unnecessary re-renders | Implement Equatable |
@State for derived values |
Redundant state, sync issues | Use computed properties |
@EnvironmentObject for local state |
All consumers re-render together | Scope to minimal views |
Code Review Process
Find SwiftUI view files:
find . -name "*.swift" -exec grep -l "struct.*View" {} \;For each view, check:
- What state properties does it observe? (
@State,@ObservedObject,@EnvironmentObject) - Does it pass whole objects to child views?
- Does it have timers or frequent updates?
- Could it implement
Equatable?
- What state properties does it observe? (
Apply fixes from
references/fix-patterns.md
Example Review
// ANTI-PATTERN: Whole object passing
struct UserList: View {
@ObservedObject var viewModel: AppViewModel // Large object
var body: some View {
ForEach(viewModel.users) { user in
UserRow(user: user) // Passes entire User object
}
}
}
// FIXED: Pass only needed data
struct UserList: View {
@ObservedObject var viewModel: AppViewModel
var body: some View {
ForEach(viewModel.users) { user in
UserRow(name: user.name, avatarURL: user.avatarURL)
}
}
}
Mode 2: Record & Analyze Trace (Deeper Insights)
For quantitative data on actual re-render counts and update chains, help the user record and analyze a trace.
Step 1: Check Prerequisites
# Check if swiftuice is installed
swiftuice version
# If not installed:
go install github.com/greenstevester/swiftui-cause-effect-cli/cmd/swiftuice@latest
Step 2: Find the App Bundle ID
Help the user find their app's bundle ID:
# From Xcode project
grep -r "PRODUCT_BUNDLE_IDENTIFIER" *.xcodeproj/project.pbxproj 2>/dev/null | head -1
# Or check the Info.plist
find . -name "Info.plist" -exec grep -A1 "CFBundleIdentifier" {} \; 2>/dev/null | head -5
# If app is running in simulator
xcrun simctl listapps booted 2>/dev/null | grep -A1 CFBundleIdentifier | head -10
Step 3: Record the Trace
Guide the user through recording:
# Record for 15 seconds - user should interact with the app during this time
swiftuice record -app <BUNDLE_ID> -time 15s -out trace.trace
Important instructions for the user:
- Make sure the app is running on a simulator or device
- During the 15 seconds, perform the UI actions that feel slow
- Focus on the specific screens/flows with performance issues
Step 4: Analyze the Trace
# Analyze with source correlation
swiftuice analyze -in trace.trace -source . -stdout
Alternative: Manual Recording in Instruments
If swiftuice record doesn't work, guide the user through Instruments:
- Open Instruments: Xcode → Open Developer Tool → Instruments
- Choose the SwiftUI template
- Select target device/simulator and app
- Click Record (red button)
- Interact with the app for 10-15 seconds
- Click Stop
- File → Save as
.tracefile
Then analyze:
swiftuice analyze -in /path/to/saved.trace -source . -stdout
Workflow
Step 1: Identify the Input
Determine what input is available:
| Input Type | Command |
|---|---|
.trace file |
swiftuice analyze -in path/to/trace.trace |
| Exported directory | swiftuice analyze -in path/to/exported/ |
| Need to record | swiftuice record -app <bundle-id> first |
Step 2: Run Analysis
Execute the analysis with source correlation if Swift source is available:
# With source correlation (recommended)
swiftuice analyze -in <trace-or-export> -source <swift-project-root> -stdout
# Without source correlation
swiftuice analyze -in <trace-or-export> -stdout
The -stdout flag outputs JSON directly for parsing.
Step 3: Parse the JSON Output
The output contains these key sections:
{
"summary": {
"performance_score": 65,
"health_status": "warning",
"issues_found": 3,
"critical_issues": 1
},
"issues": [...],
"source_correlations": [...],
"agent_instructions": {...}
}
Step 4: Implement Fixes
For each issue in the issues array:
- Check the
severity(critical, high, medium, low) - Review
suggested_fixeswithcode_beforeandcode_afterexamples - Use
source_correlationsto navigate to the affected files - Apply the fix pattern that best matches the codebase
Issue Types and Fix Patterns
Excessive Re-render (excessive_rerender)
Problem: A view updates too frequently (>10 times per user action)
Fix approaches:
- Implement Equatable on the View to control when it re-renders
- Extract subview to isolate frequently-updating content
- Use @Observable (iOS 17+) for fine-grained observation
Example fix:
// Before
struct ItemRow: View {
let item: Item
var body: some View { ... }
}
// After - Add Equatable
struct ItemRow: View, Equatable {
let item: Item
var body: some View { ... }
static func == (lhs: ItemRow, rhs: ItemRow) -> Bool {
lhs.item.id == rhs.item.id
}
}
Cascading Update (cascading_update)
Problem: Single state change triggers many view updates
Fix approaches:
- Use derived state with computed properties
- Split state into focused objects
- Scope ObservableObject to only views that need it
Timer Cascade (timer_cascade)
Problem: Timer/animation causes broad UI updates
Fix approaches:
- Use TimelineView for time-based content
- Limit timer scope to only the view that needs it
- Extract animated content into isolated subview
Whole-Object Passing (whole_object_passing)
Problem: Passing entire model objects causes unnecessary re-renders
Fix approaches:
- Pass primitives instead of whole objects
- Define focused protocols with minimal data requirements
- Use @Bindable (iOS 17+) for specific properties
Recording a New Trace
If user needs to create a trace:
# Record for 15 seconds
swiftuice record -app com.company.appname -time 15s -out trace.trace
# Then analyze
swiftuice analyze -in trace.trace -source ./MyApp -stdout
Human-Readable Summary
For user-facing summary instead of JSON:
swiftuice summarize -in <trace-or-export> -out summary.md -dot graph.dot
This creates:
summary.md: Markdown summary with top issuesgraph.dot: Graphviz diagram (render withdot -Tpng graph.dot -o graph.png)
Verification After Fixes
After implementing fixes:
- Record a new trace with the same user flow
- Run analysis again
- Compare
performance_score(should increase) - Verify
issues_founddecreased - Check specific view update counts reduced
Troubleshooting
"no parseable Cause & Effect data found"
- The SwiftUI instrument may not have captured data
- Try recording longer or with more UI interaction
- Open trace in Instruments to verify data exists
swiftuice not found
- Install with:
go install github.com/greenstevester/swiftui-cause-effect-cli/cmd/swiftuice@latest - Or add Go bin to PATH:
export PATH=$PATH:$(go env GOPATH)/bin
Source correlation shows 0 matches
- Verify
-sourcepoints to the Swift project root - Check that .swift files exist in the directory
- Source correlation uses symbol matching, works best with clear View names
References
For detailed fix patterns with more code examples, see:
references/fix-patterns.md- Complete fix pattern catalogreferences/issue-types.md- Detailed issue type documentation