Install
$ agentstack add skill-mwd1234-ios-agentic-skills-performance ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Performance & Battery Audit
Scan for layout thrash, heavy animations, timers, and background tasks. Flag hotspots and suggest fixes.
When to use this skill
- Before App Store submission
- After adding complex animations or layouts
- When users report lag, jank, or battery drain
- When reviewing Views with frequent state changes
Quick Reference: Common Issues
| Issue | Impact | Fix | |-------|--------|-----| | GeometryReader in ScrollView | Layout thrash | Use containerRelativeFrame or cache sizes | | Timer in View body | Battery drain | Use .onReceive with shared timer | | Large images in List | Memory spike | Use LazyVStack, async image loading | | Animation without value: | CPU/GPU | Scope animations with value: parameter | | .drawingGroup() everywhere | GPU memory | Use only for complex overlapping content |
Step 1: Layout Performance Audit
1.1 GeometryReader Misuse
# GeometryReader (expensive layout pass)
rg --type swift -g '!*Tests*' 'GeometryReader' YourApp/Views/ | head -15
Issues: GeometryReader inside ScrollView/List causes relayout on scroll.
Fix (iOS 17+):
// ❌ Expensive
GeometryReader { geo in content.frame(width: geo.size.width * 0.8) }
// ✅ Modern alternative
content.containerRelativeFrame(.horizontal) { width, _ in width * 0.8 }
1.2 Layout Invalidation in Body
# Inline expensive object creation
rg --type swift -g '!*Tests*' 'DateFormatter\(\)|NumberFormatter\(\)|JSONDecoder\(\)' YourApp/Views/
Fix: Move to static/cached properties.
Step 2: Animation Performance
2.1 Animation Scope
# Animations without value parameter (animate everything)
rg --type swift -g '!*Tests*' '\.animation\([^,)]+\)' YourApp/Views/ | grep -v 'value:' | head -15
Fix: Always scope animations:
.animation(.easeInOut, value: isExpanded) // ✅ Only animates this change
2.2 Heavy/Repeating Animations
# Repeating animations (battery impact)
rg --type swift -g '!*Tests*' '\.repeatForever|\.repeat\(' YourApp/Views/
# Canvas/TimelineView (GPU intensive)
rg --type swift -g '!*Tests*' 'Canvas\s*\{|TimelineView' YourApp/Views/
> watchOS: TimelineView(.periodic) is extremely battery-heavy. Prefer .animation schedule or pause when not visible.
2.3 Expensive Rendering Modifiers
# GPU-heavy modifiers
rg --type swift -g '!*Tests*' '\.drawingGroup\(|\.shadow\(radius:|\.blur\(|\.mask\s*\{|\.compositingGroup\(' YourApp/Views/ | head -20
Guidance:
.drawingGroup()— Use only for complex overlapping content, not everywhere.shadow(radius:)— Expensive with large radii; cache or use images.blur()— Very expensive; consider pre-blurred assets.mask { }— Recalculates every frame; avoid in animations.compositingGroup()— Use sparingly for correct blending
Step 3: ForEach & List Efficiency
3.1 Unstable ForEach IDs
# ForEach with \.self on non-Hashable-stable data (causes full diff)
rg --type swift -g '!*Tests*' 'ForEach.*\\\.self' YourApp/Views/ | head -15
# ForEach without explicit id:
rg --type swift -g '!*Tests*' 'ForEach\([^)]+\)\s*\{' YourApp/Views/ | grep -v 'id:' | head -15
Issues:
\.selfon arrays that get reordered/modified causes full re-render- Missing stable IDs cause diffing cost
Fix: Use stable, unique identifiers:
ForEach(items, id: \.stableID) { item in ... }
3.2 LazyVStack/LazyHStack Usage
# Check for lazy stacks in lists
rg --type swift -g '!*Tests*' 'LazyVStack|LazyHStack|LazyVGrid|LazyHGrid' YourApp/Views/
# Non-lazy stacks in ScrollView (loads all content)
rg --type swift -g '!*Tests*' -B2 'ScrollView' YourApp/Views/ | grep -A2 'VStack\|HStack' | head -20
Step 4: State Management
4.1 @State Arrays/Dictionaries
# Large state objects in Views
rg --type swift -g '!*Tests*' '@State\s+(private\s+)?var\s+\w+:\s*\[' YourApp/Views/ | head -15
Fix: Large collections should be in ViewModel/ObservableObject, not View @State.
4.2 @StateObject Initialization
# @StateObject in body (wrong - should be in init or property)
rg --type swift -g '!*Tests*' -B3 '@StateObject' YourApp/Views/ | head -20
Fix: Initialize @StateObject in property declaration or init, never in body:
// ✅ Correct
@StateObject private var viewModel = MyViewModel()
// ❌ Wrong - recreates on every render
var body: some View {
let vm = MyViewModel() // BAD
}
Step 5: Timer & Background Task Audit
5.1 Timer Usage
# Timer instances
rg --type swift -g '!*Tests*' 'Timer\.|Timer\.publish|Timer\.scheduledTimer' YourApp/
Issues:
- Timers not invalidated when view disappears
- High-frequency timers ( watchOS priority: Battery > Smoothness.
TimelineView(.periodic)drains battery fast—use.animationschedule instead.
Step 8: Instruments Verification
For issues found, verify with Instruments:
- Time Profiler — CPU hotspots
- Core Animation — Frame drops, offscreen renders
- Allocations — Memory leaks, spikes
- Energy Log — Battery impact
Skip Patterns
*Tests*/,*UITests*/#Preview { }blocks
Output Format
| Category | Issues | Status | |----------|--------|--------| | GeometryReader usage | X | ✅/❌ | | Animation scope | X | ✅/❌ | | Expensive modifiers | X | ✅/❌ | | ForEach IDs | X | ✅/❌ | | @StateObject init | X | ✅/❌ | | Timer management | X | ✅/❌ | | Watch app battery | X | ✅/❌ |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: mwd1234
- Source: mwd1234/ios-agentic-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.