
metrickit
Use when collecting or analyzing production iOS or iPadOS performance telemetry with MetricKit, including iOS 27 MetricManager async metric or diagnostic reports, hang or crash triage, custom signposts, extended launch measurement, durable export, or iOS 26 MXMetricManager compatibility.
- Installs
- 3.2K
- Last 24h
- +16
- Trending rank
- #6824
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill metrickit- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads metrickit automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
MetricKit
Use MetricKit for low-overhead production telemetry that complements local Instruments and Xcode Organizer analysis. On iOS and iPadOS 27, prefer the Swift-first MetricManager report sequences. Keep MXMetricManager only in an explicit iOS 26 compatibility branch.
Beta-sensitive: The iOS/iPadOS 27 surface below is based on Apple's current beta documentation. It has not been locally compiler-verified because Xcode 27 is unavailable in this environment. Re-check the linked Apple documentation and compile with the shipping Xcode 27 SDK before release.
Load MetricKit Extended and Compatibility Patterns when implementing durable ingestion, detailed report analysis, or the iOS 26 compatibility path.
Contents
- MetricManager Setup
- Receiving Metric Reports
- Receiving Diagnostic Reports
- Key Metric Results
- Call Stack Trees
- Custom Signpost Metrics
- Durable Export and Upload
- Extended Launch Measurement
- iOS 26 Compatibility
- Xcode Organizer
- Scope Boundaries
- Common Mistakes
- Review Checklist
- References
MetricManager Setup
At app launch, create and retain one long-lived MetricManager. Start exactly one consumer task for metricReports and one for diagnosticReports.
Both properties expose nonthrowing AsyncSequence values:
metricReports: some AsyncSequence<MetricReport, Never>diagnosticReports: some AsyncSequence<DiagnosticReport, Never>
Apple documents that concurrent consumers of one sequence can receive nondeterministic subsets. Fan out only after the single consumer receives and durably stores a report; delayed subscription can miss reports.
import MetricKit
@available(iOS 27.0, *)
final class MetricsService {
private let manager = MetricManager()
private var metricTask: Task<Void, Never>?
private var diagnosticTask: Task<Void, Never>?
func start(
persistMetric: @escaping @Sendable (MetricReport) async -> Void,
persistDiagnostic: @escaping @Sendable (DiagnosticReport) async -> Void
) {
guard metricTask == nil, diagnosticTask == nil else { return }
let manager = manager
metricTask = Task {
for await report in manager.metricReports {
await persistMetric(report)
}
}
diagnosticTask = Task {
for await report in manager.diagnosticReports {
await persistDiagnostic(report)
}
}
}
deinit {
metricTask?.cancel()
diagnosticTask?.cancel()
}
}
The persistence closures are application-specific. Implement them with the durable-first workflow below rather than dropping, logging only, or directly uploading each report. If state-scoped metrics are needed, construct the manager with the documented init(enabledStateReportingDomains:) initializer and the required domains.
Receiving Metric Reports
MetricReport is Codable and Sendable. It describes an interval through:
timeRange: DateInterval- optional
environmentmetadata intervalEntriesfor full-day and shorter interval measurementsstateEntriesfor measurements associated with application states
Metric reports normally arrive on a daily cadence. Persist the complete report before extracting individual results.
For daily analysis, read the documented fullDayEntry and switch over its MetricResult values:
let entry = report.intervalEntries.fullDayEntry
for result in entry.values {
switch result {
case .hangTime(let metric):
analyzeHangTime(metric)
case .peakMemory(let metric):
analyzePeakMemory(metric)
case .timeToFirstDraw(let metric):
analyzeLaunch(metric)
case .signpostInterval(let metric):
analyzeSignpost(metric)
@unknown default:
preserveUnknownMetric(result)
}
}
Use @unknown default so a beta or future result does not make the ingestion pipeline brittle. Preserve the raw encoded report even when the current app does not understand a result.
Receiving Diagnostic Reports
DiagnosticReport is Codable and Sendable. It contains a timeRange, required environment metadata, and one DiagnosticResult.
Diagnostics are individual, event-based reports intended for prompt delivery when MetricKit produces them. Do not assume every crash, hang, or resource event generates a report; system sampling and eligibility still apply.
After durable storage, route the result explicitly:
switch report.result {
case .crash(let diagnostic):
analyzeCrash(diagnostic)
case .hang(let diagnostic):
analyzeHang(diagnostic)
case .cpuException(let diagnostic):
analyzeCPUException(diagnostic)
case .diskWriteException(let diagnostic):
analyzeDiskWrites(diagnostic)
case .appLaunch(let diagnostic):
analyzeLaunch(diagnostic)
case .memoryException(let diagnostic):
analyzeMemory(diagnostic)
@unknown default:
preserveUnknownDiagnostic(report)
}
The iOS/iPadOS 27 diagnostic types are CrashDiagnostic, HangDiagnostic, CPUExceptionDiagnostic, DiskWriteExceptionDiagnostic, AppLaunchDiagnostic, and MemoryExceptionDiagnostic. The memory-exception case is new in iOS 27.
Useful fields include:
| Diagnostic | Important fields |
|---|---|
CrashDiagnostic | callStackTree, exception type/code/reason, signal, virtual-memory region, termination category/reason |
HangDiagnostic | callStackTree, hangDuration |
CPUExceptionDiagnostic | callStackTree, totalCPUTime, totalSampledTime |
DiskWriteExceptionDiagnostic | callStackTree, totalBytesWritten |
AppLaunchDiagnostic | callStackTree, launchDuration |
MemoryExceptionDiagnostic | callStackTree |
Key Metric Results
Choose a small telemetry vocabulary that matches the investigation rather than exporting every result. Prioritize the relevant categories: responsiveness and terminations; runtime, CPU, memory, network, and storage; or launch, display/GPU, and custom intervals.
Aggregate by app version and environment metadata, compare distributions rather than single values, and correlate regressions with releases. Avoid treating a daily aggregate as a precise trace of one user action.
Load the Key Metric Result Catalog when mapping exact MetricResult cases into a parser or dashboard.
Call Stack Trees
The iOS 27 CallStackTree replaces MXCallStackTree. It is Codable and Sendable and exposes:
forEachFramefor frame traversalcallStackThreadsfor thread-oriented analysisbinaryInfofor image and binary metadata
Store the complete diagnostic report before flattening or symbolication. Preserve binary identifiers and offsets so server-side symbolication can use matching archives and dSYMs.
Custom Signpost Metrics
Create an OS log through the manager, then use mxSignpost for begin/end measurement:
let log = manager.logHandle(category: "ImagePipeline")
mxSignpost(.begin, log: log, name: "Decode")
await decodeImage()
mxSignpost(.end, log: log, name: "Decode")
MetricKit exposes the aggregate as MetricResult.signpostInterval. Do not search for a signpostMetrics array on MetricReport.
Use mxSignpost when MetricKit resource measurement is required. Apple documents that intervals created with OSSignposter and a MetricKit log handle do not populate the resource-measurement fields that mxSignpost provides.
Keep high-volume local tracing separate from the small set of stable production intervals used for MetricKit aggregation.
Durable Export and Upload
Treat report delivery as an at-least-once ingestion problem:
- Encode the complete
MetricReportorDiagnosticReportwithJSONEncoder. - Atomically enqueue the bytes in an app-owned durable outbox.
- Record report type, schema version, app version, and a stable deduplication key.
- Acknowledge local processing only after the enqueue succeeds.
- Upload later with retry, backoff, batching, and retention limits.
- Mark the outbox item uploaded only after the server accepts it.
let data = try JSONEncoder().encode(report)
try await durableOutbox.enqueue(data, kind: .metric)
durableOutbox is an application-owned abstraction, not a MetricKit API. Do not perform a synchronous network upload inside the sequence consumer.
Durable local storage is the recovery mechanism for reports received through the modern sequences; do not make successful ingestion depend on an assumed modern backfill API.
Extended Launch Measurement
Use trackLaunchTask(id:onTrackingError:_:) on the manager for work that extends beyond time to first draw:
await manager.trackLaunchTask(
id: "bootstrap-data",
onTrackingError: { error in
recordLaunchTrackingError(error)
}
) {
await bootstrapApplication()
}
The API is @MainActor and has synchronous and asynchronous overloads. The task closure's result and thrown error propagate to the caller; a MetricManager.LaunchTaskError is reported through onTrackingError without interrupting the tracked work. Results appear as MetricResult.extendedLaunch.
Use stable LaunchTaskID values and track only launch-critical work.
iOS 26 Compatibility
For an app that still deploys to iOS 26, use an availability boundary:
- iOS/iPadOS 27: use
MetricManagerand consume both async report sequences. - iOS/iPadOS 26 and earlier supported releases: use
MXMetricManager.shared, anMXMetricManagerSubscriber, and the legacy payload callbacks.
The legacy APIs remain the only documented branch with pastPayloads and pastDiagnosticPayloads. MXMetricManager is deprecated in iOS 27, so isolate it behind availability checks rather than mixing legacy payloads into the modern pipeline.
Load iOS 26 Compatibility for the full subscriber, legacy signpost, past-payload, and extended-launch patterns.
Xcode Organizer
Use Xcode Organizer to inspect Apple-aggregated production metrics, hangs, crashes, and regressions before building a custom backend. Use direct MetricKit ingestion when the product needs custom correlation, retention, alerting, or integration with an existing observability system.
Do not expect development-device runs to reproduce the population, cadence, or aggregation of production reports.
Scope Boundaries
| Task | Use instead |
|---|---|
| Reproduce a problem locally or record a detailed trace | debugging-instruments |
| Diagnose retained objects or memory graph paths | ios-memgraph-analysis |
| Tune SwiftUI invalidation, identity, or scrolling code | swiftui-performance |
| Study structured EnergyKit impact data | energykit |
| Design general logging and os_signpost strategy | swift-logging |
MetricKit identifies production symptoms and trends. Route the actual code fix to the skill that owns the affected subsystem.
Common Mistakes
| Mistake | Correction |
|---|---|
| Uploading directly in the sequence loop | Encode and enqueue locally first; upload asynchronously later. |
| Assuming every diagnostic event produces a report | Treat diagnostics as sampled, system-produced evidence. |
Looking for pastPayloads on MetricManager | Keep legacy backfill only in the iOS 26 MXMetricManager branch. |
| Parsing only known enum cases | Preserve the raw report and use @unknown default. |
Using MXMetricManager.makeLogHandle in the iOS 27 branch | Use manager.logHandle(category:). |
| Using paired extend/finish launch calls in the iOS 27 branch | Use trackLaunchTask. |
| Treating MetricKit aggregates as local traces | Reproduce with Instruments and signposts. |
Review Checklist
- The iOS 27 branch starts at launch with one retained
MetricManagerand one consumer per report sequence. - Every report is encoded and durably enqueued before analysis or upload.
- Diagnostic and metric switches use
@unknown default. - Unknown raw reports remain recoverable.
- Symbolication metadata and matching dSYMs are retained.
- Custom intervals use
manager.logHandle(category:)andmxSignpost. - Extended launch work uses
trackLaunchTask. - The iOS 26
MXMetricManagerpath is isolated behind availability checks. - Production aggregates lead to a local Instruments investigation when appropriate.
- iOS 27 beta APIs have been rechecked and compiled with the shipping Xcode 27 SDK.
References
- MetricManager
- Metric report sequence
- Diagnostic report sequence
- MetricReport
- DiagnosticReport
- MetricResult
- DiagnosticResult
- CallStackTree
- Monitoring app performance with MetricKit
- Analyzing app performance with MetricKit
- iOS & iPadOS 27 release notes
- What's new in MetricKit (WWDC26)
- MetricKit Extended and Compatibility Patterns
FAQ
What does the metrickit skill do?
Use when collecting or analyzing production iOS or iPadOS performance telemetry with MetricKit, including iOS 27 MetricManager async metric or diagnostic reports, hang or crash triage, custom signposts, extended launch measurement, durable export, or iOS 26 MXMetricManager compatibility.
How do I install metrickit?
Run npx skills add https://github.com/dpearson2699/swift-ios-skills --skill metrickit in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/metrickit/ (personal) or .claude/skills/metrickit/ (project).
Which AI agents can use metrickit?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is metrickit safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains metrickit?
It is published by dpearson2699 in dpearson2699/swift-ios-skills. It has 3,168 recorded installs.
More from dpearson2699/swift-ios-skills
swiftui-animation
dpearson2699/swift-ios-skills
Implement, diagnose, or review SwiftUI motion using explicit and scoped implicit animations, springs, transitions, PhaseAnimator, KeyframeAnimator, matched geometry or navigation zoom, SF Symbol effects, and custom Animation types. Use when views should animate on state changes, insertion, removal, navigation, or multi-step choreography, or when motion must respect Reduce Motion and Swift concurrency.
ios-accessibility
dpearson2699/swift-ios-skills
Build and audit SwiftUI, UIKit, and AppKit accessibility for VoiceOver, Voice Control, Switch Control, Full Keyboard Access, Dynamic Type, focus restoration, labels/traits/actions, traversal, custom rotors, NSAccessibility, XCTest checks, adaptive system preferences, and App Store accessibility declarations. Use when implementing accessible UI, fixing an accessibility audit, testing assistive-technology behavior, or substantiating App Store Accessibility Nutrition Labels.
swiftui-performance
dpearson2699/swift-ios-skills
Profile, diagnose, and remediate SwiftUI runtime performance using code review, Instruments, and repeatable measurements. Use when a SwiftUI screen renders slowly, scrolling or animations hitch, view bodies update excessively, list identity churns, layout work spikes, or broad Observation dependencies raise CPU cost. Covers evidence-based triage, SwiftUI Instruments lanes, lazy-container guardrails, state lifetime, and before/after verification.
swiftui-patterns
dpearson2699/swift-ios-skills
Builds and reviews SwiftUI views with modern MV architecture, state, composition, isolated previews, and migration guidance. Covers @Observable ownership, @State/@Bindable/@Environment wiring, view decomposition, ViewModifiers, environment values, .task loading, iOS 26+ handoffs, Writing Tools, clipboard availability, and performance. Use when structuring SwiftUI state, managing @Observable, composing views, previewing meaningful UI states, or correcting SwiftUI patterns.
app-store-review
dpearson2699/swift-ios-skills
Audits App Store submission readiness and rejection risk across current review guidelines, PrivacyInfo.xcprivacy and required-reason APIs, privacy labels, ATT, StoreKit payments, metadata, entitlements, widgets, and Live Activities. Use when preparing a submission, responding to rejection, reconciling privacy evidence, or separating upload blockers from cleanup.
ios-networking
dpearson2699/swift-ios-skills
Build, review, or improve networking code in iOS/macOS apps using URLSession with async/await, structured concurrency, and modern Swift patterns. Use when working with REST APIs, downloading files, uploading data, WebSocket connections, pagination, retry logic, request middleware, caching, background transfers, or network reachability monitoring. Also use when handling HTTP requests, API clients, network error handling, or data fetching in Swift apps.
Popular mobile skills
design-mobile-apps
designed-by-ai/skills
Design mobile app screens with Sleek, edit Sleek projects, and implement their designs in React Native or HTML.
ios-design
uizze.sh
Design and review iOS UI/UX for iPhone apps in SwiftUI, UIKit, or React Native. Use for mobile app design, screen redesigns, navigation flows, typography, forms, keyboard and safe-area layout, and accessibility checks. Apply Apple Human Interface Guidelines and consult real product references through the optional Uizze connection.
vercel-react-native-skills
vercel-labs/agent-skills
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
animate-expo
emilkowalski/skills
Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it animate, which thread it runs on, which properties, spring or timing, how the gesture hands off, how it degrades. Writes the implementation with Reanimated, Gesture Handler, Expo Router and expo-haptics. Use when animating anything in an Expo app, adding gestures, sheets, screen transitions, press feedback or haptics, or fixing motion that stutters on device. For web animation use animate.
xcode-project-setup
firebase/agent-skills
Safely modifies Xcode projects (.pbxproj) to add Swift Packages and link files. Use this skill whenever an iOS project needs dependencies installed (e.g. Firebase, Alamofire).
firebase-remote-config-basics
firebase/agent-skills
Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Firebase Hosting, Auth, Firestore, Data Connect, Crashlytics, or App Hosting.