skills/performance/profiling/SKILL.md
Guide performance profiling with Instruments, diagnose hangs, memory issues, slow launches, and energy drain. Use when reviewing app performance or investigating specific bottlenecks.
npx skillsauth add rshankras/claude-code-apple-skills performance-profilingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Systematic guide for profiling Apple platform apps using Instruments, Xcode diagnostics, and MetricKit. Covers CPU, memory, launch time, and energy analysis with actionable fix patterns.
Use this skill when the user:
os_signpost or performance measurement to codeWhat performance problem are you investigating?
│
├─ App hangs / unresponsive to taps / slow UI
│ └─ Read time-profiler.md
│
├─ Scroll or animation stutter / dropped frames / hitches
│ └─ Read hitches.md
│
├─ High memory / leaks / OOM crashes / growing footprint
│ └─ Read memory-profiling.md
│
├─ Slow app launch / time to first frame
│ └─ Read launch-optimization.md
│
├─ Battery drain / thermal throttling / background energy
│ └─ Read energy-diagnostics.md
│
├─ General "app feels slow" (unknown cause)
│ └─ Start with time-profiler.md, then memory-profiling.md
│
└─ Pre-release performance audit
└─ Read ALL reference files, use Review Checklist below
| Problem | Instrument / Tool | Key Metric | Reference | |---------|-------------------|------------|-----------| | UI hangs > 250ms | Time Profiler + Hangs | Hang duration, main thread stack | time-profiler.md | | Scroll/animation hitches | Animation Hitches template | Hitch time ratio (< 5 ms/s good, > 10 critical) | hitches.md | | High CPU usage | Time Profiler / CPU Profiler | CPU % by function, call tree weight | time-profiler.md | | Memory leak | Leaks + Memory Graph | Leaked bytes, retain cycle paths | memory-profiling.md | | Memory growth | Allocations | Live bytes, generation analysis | memory-profiling.md | | Slow launch | App Launch | Time to first frame (pre-main + post-main) | launch-optimization.md | | Battery drain | Energy Log / Power Profiler | Energy Impact score, CPU/GPU/network | energy-diagnostics.md | | Thermal issues | Activity Monitor | Thermal state transitions | energy-diagnostics.md | | Network waste | Network profiler | Redundant fetches, large payloads | energy-diagnostics.md |
Apple enumerates exactly eight things to track for app performance, all coverable with five tools (Xcode Organizer, MetricKit, Instruments, XCTest, App Store Connect API):
| # | Metric | Threshold / signal | Tool | |---|--------|--------------------|------| | 1 | Battery usage | Energy Gauge flags CPU use > 20% as High CPU, plus CPU Wake Overhead regions; top subsystems to watch: CPU, Networking, Location | Energy Gauge → Time Profiler | | 2 | Launch time | Time between icon tap and first frame rendered | App Launch template, XCTApplicationLaunchMetric | | 3 | Hang rate | Unresponsive to input for ≥ 250ms | Hangs instrument, Organizer | | 4 | Memory | Organizer charts peak memory and memory at suspension; a spike with no termination spike yet is your early warning | Allocations, Leaks, VM Tracker | | 5 | Disk writes | Exception report when the app writes > 1 GB in 24 hours (with stack trace + Insights annotations) | File Activity template, XCTStorageMetric | | 6 | Scrolling | Red bars in the Organizer scrolling chart = poor scroll experience, fix immediately | Animation Hitches, XCTOSSignpostMetric.scrollDecelerationMetric | | 7 | Terminations | Every termination forces a slow cold launch next time and loses user state | MXAppExitMetric, Organizer | | 8 | MXSignposts | Custom marked intervals for your critical code sections | MetricKit |
The Organizer aggregates all of these from consented user devices across the last 16 app versions; the Regressions pane (Xcode 13) isolates every metric that increased significantly in the most recent version, in one place. The same data is available as JSON via the App Store Connect API (WWDC21 10181).
Ask the user or inspect their description to classify the issue:
Each file contains:
Always remind users:
After identifying bottlenecks:
os_signpost markers for ongoing monitoringRecommend enabling these in Scheme > Run > Diagnostics:
| Setting | What It Catches | |---------|-----------------| | Main Thread Checker | UI work off main thread | | Thread Sanitizer | Data races | | Address Sanitizer | Buffer overflows, use-after-free | | Malloc Stack Logging | Memory allocation call stacks | | Zombie Objects | Messages to deallocated objects |
For production monitoring, recommend MetricKit (WWDC20 10081, WWDC21 10181):
import MetricKit
final class PerformanceReporter: NSObject, MXMetricManagerSubscriber {
func startCollecting() {
MXMetricManager.shared.add(self) // subscribe once, early in launch
}
// best practice: remove(self) in deinit (WWDC21 10181)
func didReceive(_ payloads: [MXMetricPayload]) {
for payload in payloads {
// Launch time
if let launch = payload.applicationLaunchMetrics {
log("Resume time: \(launch.histogrammedResumeTime)")
}
// Hang rate
if let responsiveness = payload.applicationResponsivenessMetrics {
log("Hang time: \(responsiveness.histogrammedApplicationHangTime)")
}
// Memory
if let memory = payload.memoryMetrics {
log("Peak memory: \(memory.peakMemoryUsage)")
}
// Scroll hitches (ratio of time hitching to time scrolling)
if let animation = payload.animationMetrics {
log("Scroll hitch ratio: \(animation.scrollHitchTimeRatio)")
}
// Exit reasons — daily counts per termination cause, fg + bg
if let exits = payload.applicationExitMetrics {
log("Exits: \(exits.backgroundExitData)")
}
}
}
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for payload in payloads {
if let hangs = payload.hangDiagnostics {
for hang in hangs {
log("Hang: \(hang.callStackTree)")
}
}
}
}
}
Key facts (WWDC20 10081, WWDC21 10181):
MXHistogram). On iOS 15+/macOS 12+, all diagnostics are delivered immediately after the issue occurs instead of daily.MXHangDiagnostic (main-thread unresponsive time + backtraces), MXCrashDiagnostic (exception info, termination reason, VM region info), MXCPUExceptionDiagnostic (threads burning CPU — the programmatic form of Organizer energy logs), MXDiskWriteExceptionDiagnostic (fires on the 1 GB/day write threshold).MXCallStackTree backtraces are unsymbolicated by design — symbolicate off-device with atos-class tools + your dSYMs; ❌ don't try on-device.MXSignpost marks critical code sections for field telemetry; the animation variant captures hitch-rate telemetry for the interval:let handle = MXMetricManager.makeLogHandle(category: "animation_telemetry")
mxSignpostAnimationIntervalBegin(log: handle, name: "custom_animation")
// ... animation ...
mxSignpost(OSSignpostType.end, log: handle, name: "custom_animation")
.preparingThumbnail or async decoding)@MainActor only on code that truly needs UI accessself)autoreleasepool used in tight loops creating ObjC objectsinit() of @main App structBGProcessingTaskRequest appropriately.best)tolerance to allow coalescingdevelopment
US web checkout via the StoreKit External Purchase Link entitlement — currently 0% Apple commission (litigation ongoing), how to ship it safely, and how to architect for a commission flip so a future ruling is a config change, not a rewrite. Use when adding external purchase links, weighing web checkout vs IAP, or planning US-storefront pricing strategy.
tools
Revenue beyond the single-app price tag — own-app bundles, Family Sharing as a conversion lever, cross-developer bundles & suites, and institutional licensing via Group Purchases / Apple School & Business Manager. Use when a developer has multiple apps, a subscription worth sharing, complementary indie partners, or school/clinic/business buyers.
testing
Run a structured accessibility audit on an iOS/macOS app — automated XCUITest audits, Accessibility Inspector, manual VoiceOver/Dynamic Type passes, and App Store Accessibility Nutrition Label evaluation. Use before release, when preparing Nutrition Label declarations, or for EU Accessibility Act compliance.
tools
Stage-by-stage audit of an app's App Store growth machinery against a 54-item P0–P9 playbook — every item scored from an App Store Connect MCP call, a codebase check, or an explicit question to the user, then routed to the skill or command that fixes it. Read-only on App Store Connect. Use for a growth audit or scorecard, a pre-launch growth plan, a quarterly re-audit, or "which growth levers am I missing."