swiftuice-analyze
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.
SKILL.md
| 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. |
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