Claude
Skills
Sign in
Back

metrickit

Included with Lifetime
$97 forever

Collect and analyze on-device performance metrics and crash diagnostics using MetricKit. Use when setting up MXMetricManager, handling MXMetricPayload or MXDiagnosticPayload, processing crash/hang/disk-write diagnostics via MXCallStackTree, adding custom signpost metrics, correcting mxSignpost or extended launch measurement code, or uploading telemetry to an analytics backend.

Backend & APIs

What this skill does


# MetricKit

Collect aggregated performance metrics and crash diagnostics from production
devices using MetricKit. The framework delivers daily metric payloads (CPU,
memory, launch time, hang rate, animation hitches, network usage) and
diagnostic payloads (crashes, hangs, disk-write exceptions) with call-stack
trees for triage.

## Contents

- [Subscriber Setup](#subscriber-setup)
- [Receiving Metric Payloads](#receiving-metric-payloads)
- [Receiving Diagnostic Payloads](#receiving-diagnostic-payloads)
- [Key Metrics](#key-metrics)
- [Call Stack Trees](#call-stack-trees)
- [Custom Signpost Metrics](#custom-signpost-metrics)
- [Exporting and Uploading Payloads](#exporting-and-uploading-payloads)
- [Extended Launch Measurement](#extended-launch-measurement)
- [Xcode Organizer Integration](#xcode-organizer-integration)
- [Scope Boundaries](#scope-boundaries)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)

## Subscriber Setup

Register a subscriber as early as possible — ideally in
`application(_:didFinishLaunchingWithOptions:)` or `App.init`. MetricKit
starts accumulating reports after the first access to `MXMetricManager.shared`.
When backfilling, state precisely that `pastPayloads` and
`pastDiagnosticPayloads` return reports generated since the last allocation of
the shared manager instance.

```swift
import MetricKit

final class MetricsSubscriber: NSObject, MXMetricManagerSubscriber {
    static let shared = MetricsSubscriber()

    func subscribe() {
        let manager = MXMetricManager.shared
        manager.add(self)

        // Reports generated since the last allocation of the shared manager.
        processMetricPayloads(manager.pastPayloads)
        processDiagnosticPayloads(manager.pastDiagnosticPayloads)
    }

    func unsubscribe() {
        MXMetricManager.shared.remove(self)
    }

    func didReceive(_ payloads: [MXMetricPayload]) {
        processMetricPayloads(payloads)
    }

    func didReceive(_ payloads: [MXDiagnosticPayload]) {
        processDiagnosticPayloads(payloads)
    }
}
```

### UIKit Registration

```swift
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    MetricsSubscriber.shared.subscribe()
    return true
}
```

### SwiftUI Registration

```swift
@main
struct MyApp: App {
    init() {
        MetricsSubscriber.shared.subscribe()
    }
    var body: some Scene {
        WindowGroup { ContentView() }
    }
}
```

## Receiving Metric Payloads

`MXMetricPayload` arrives approximately once per 24 hours containing
aggregated metrics. The array may contain multiple payloads if prior
deliveries were missed.

```swift
func didReceive(_ payloads: [MXMetricPayload]) {
    for payload in payloads {
        let begin = payload.timeStampBegin
        let end = payload.timeStampEnd
        let version = payload.latestApplicationVersion

        // Persist raw JSON before processing
        let jsonData = payload.jsonRepresentation()
        persistPayload(jsonData, from: begin, to: end)

        enqueueMetricProcessing(jsonData)
    }
}
```

**Availability**: `MXMetricPayload` — iOS 13.0+, iPadOS 13.0+,
Mac Catalyst 13.1+, macOS 10.15+, visionOS 1.0+

## Receiving Diagnostic Payloads

`MXDiagnosticPayload` delivers crash, hang, CPU exception, disk-write, and
app-launch diagnostics where supported. On iOS 15+ and macOS 12+, supported
diagnostics can arrive as soon as available rather than bundled with the daily
report.

```swift
func didReceive(_ payloads: [MXDiagnosticPayload]) {
    for payload in payloads {
        let jsonData = payload.jsonRepresentation()
        persistPayload(jsonData)
        enqueueDiagnosticProcessing(jsonData)
    }
}
```

In the background processor, inspect the typed diagnostic arrays after the raw
payload is durable:

```swift
func processDiagnosticPayload(_ payload: MXDiagnosticPayload) {
    if let crashes = payload.crashDiagnostics {
        for crash in crashes {
            handleCrash(crash)
        }
    }
    if let hangs = payload.hangDiagnostics {
        for hang in hangs {
            handleHang(hang)
        }
    }
    if let diskWrites = payload.diskWriteExceptionDiagnostics {
        for diskWrite in diskWrites {
            handleDiskWrite(diskWrite)
        }
    }
    if let cpuExceptions = payload.cpuExceptionDiagnostics {
        for cpuException in cpuExceptions {
            handleCPUException(cpuException)
        }
    }
    #if os(iOS) || targetEnvironment(macCatalyst) || os(visionOS)
    if #available(iOS 16.0, macCatalyst 16.0, visionOS 1.0, *),
       let launchDiagnostics = payload.appLaunchDiagnostics {
        for launchDiagnostic in launchDiagnostics {
            handleSlowLaunch(launchDiagnostic)
        }
    }
    #endif
}
```

**Availability**: `MXDiagnosticPayload` — iOS 14.0+, iPadOS 14.0+,
Mac Catalyst 14.0+, macOS 12.0+, visionOS 1.0+. `appLaunchDiagnostics`
requires iOS 16.0+, iPadOS 16.0+, Mac Catalyst 16.0+, or visionOS 1.0+.

## Key Metrics

### Launch Time — MXAppLaunchMetric

```swift
if let launch = payload.applicationLaunchMetrics {
    let firstDraw = launch.histogrammedTimeToFirstDraw
    let optimized = launch.histogrammedOptimizedTimeToFirstDraw
    let resume = launch.histogrammedApplicationResumeTime
    let extended = launch.histogrammedExtendedLaunch
}
```

### Run Time — MXAppRunTimeMetric

```swift
if let runTime = payload.applicationTimeMetrics {
    let fg = runTime.cumulativeForegroundTime    // Measurement<UnitDuration>
    let bg = runTime.cumulativeBackgroundTime
    let bgAudio = runTime.cumulativeBackgroundAudioTime
    let bgLocation = runTime.cumulativeBackgroundLocationTime
}
```

### CPU, Memory, and Responsiveness

```swift
if let cpu = payload.cpuMetrics {
    let cpuTime = cpu.cumulativeCPUTime              // Measurement<UnitDuration>
}
if let memory = payload.memoryMetrics {
    let peakMemory = memory.peakMemoryUsage           // Measurement<UnitInformationStorage>
}
if let responsiveness = payload.applicationResponsivenessMetrics {
    let hangTime = responsiveness.histogrammedApplicationHangTime
}
if let animation = payload.animationMetrics {
    let scrollHitchRate = animation.scrollHitchTimeRatio  // Measurement<Unit>
}
```

### Network and Cellular

```swift
if let network = payload.networkTransferMetrics {
    let wifiUp = network.cumulativeWifiUpload          // Measurement<UnitInformationStorage>
    let wifiDown = network.cumulativeWifiDownload
    let cellUp = network.cumulativeCellularUpload
    let cellDown = network.cumulativeCellularDownload
}
```

### App Exit Metrics

```swift
if let exits = payload.applicationExitMetrics {
    let fg = exits.foregroundExitData
    let bg = exits.backgroundExitData
    // Inspect normal, abnormal, watchdog, memory, etc.
}
```

## Call Stack Trees

`MXCallStackTree` is attached to each diagnostic. Use `jsonRepresentation()` to extract frame data, then symbolicate with `atos` or by uploading dSYMs to your analytics service.

See [references/metrickit-patterns.md](references/metrickit-patterns.md) for crash/hang handling code and JSON structure details.

**Availability**: `MXCallStackTree` — iOS 14.0+, iPadOS 14.0+,
Mac Catalyst 14.0+, macOS 12.0+, visionOS 1.0+

## Custom Signpost Metrics

Use `mxSignpost` with a MetricKit log handle to capture custom performance
intervals. Leave the advanced `dso`, `signpostID`, and `format` parameters at
their documented defaults. Custom metrics appear in the daily `MXMetricPayload`
under `signpostMetrics`; call that out when reviewing custom MetricKit
instrumentation. When correcting custom signpost code, explicitly name
`MXMetricPayload.signpostMetrics` so the caller knows where the data lands.
Do not allocate or pass an `OSSignpostID` for the basic MetricKit pattern; use
the defaulted `mxSignpost(.begin/.end, log:name:)` calls unless there is a
specific overlapping-interval

Related in Backend & APIs