Skip to main content
ClaudeWave
Skill1.1k repo starsupdated 1mo ago

metrickit

MetricKit collects and analyzes aggregated on-device performance metrics and crash diagnostics from production iOS devices, delivering daily payloads containing CPU, memory, launch time, hang rates, and animation hitches alongside diagnostic data with call-stack trees. Use it to set up metric and diagnostic subscribers, process performance payloads, extract crash or hang diagnostics via call-stack trees, add custom signpost metrics for user-defined events, and upload telemetry data to analytics backends for production monitoring.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/dpearson2699/swift-ios-skills /tmp/metrickit && cp -r /tmp/metrickit/skills/metrickit ~/.claude/skills/metrickit
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# 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](references/metrickit-patterns.md) when implementing durable ingestion, detailed report analysis, or the iOS 26 compatibility path.

## Contents

- [MetricManager Setup](#metricmanager-setup)
- [Receiving Metric Reports](#receiving-metric-reports)
- [Receiving Diagnostic Reports](#receiving-diagnostic-reports)
- [Key Metric Results](#key-metric-results)
- [Call Stack Trees](#call-stack-trees)
- [Custom Signpost Metrics](#custom-signpost-metrics)
- [Durable Export and Upload](#durable-export-and-upload)
- [Extended Launch Measurement](#extended-launch-measurement)
- [iOS 26 Compatibility](#ios-26-compatibility)
- [Xcode Organizer](#xcode-organizer)
- [Scope Boundaries](#scope-boundaries)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#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.

```swift
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 `environment` metadata
- `intervalEntries` for full-day and shorter interval measurements
- `stateEntries` for 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:

```swift
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:

```swift
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` |
| `AppL
accessorysetupkitSkill

Discover and configure Bluetooth and Wi-Fi accessories using AccessorySetupKit. Use when presenting a privacy-preserving accessory picker, defining discovery descriptors for BLE or Wi-Fi devices, handling accessory session events, migrating from CoreBluetooth permission-based scanning, or setting up accessories without requiring broad Bluetooth permissions.

activitykitSkill

Implement, review, or improve Live Activities and Dynamic Island experiences in iOS apps using ActivityKit. Use when building real-time updating widgets for the Lock Screen and Dynamic Island — delivery tracking, sports scores, ride-sharing status, workout timers, media playback, or any time-sensitive information that updates in real time. Also use when working with ActivityKit, ActivityAttributes, Activity lifecycle (request/update/end), Dynamic Island layouts (compact/minimal/expanded), push-to-update Live Activities, or Lock Screen live widgets.

adattributionkitSkill

Measure ad effectiveness with privacy-preserving attribution using AdAttributionKit. Use when registering ad impressions, handling attribution postbacks, updating conversion values, implementing re-engagement attribution, configuring publisher or advertiser apps, or replacing SKAdNetwork with AdAttributionKit for ad measurement.

alarmkitSkill

Implement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple Watch system UI. Covers AlarmManager scheduling, AlarmAttributes and AlarmPresentation, system Stop and AlarmButton secondary actions, authorization, state observation, countdown widget-extension handoff, and Live Activity integration. Use when building wake-up alarms, countdown timers, or alarm-style alerts that need Apple's system alarm experience.

app-clipsSkill

Build iOS App Clips with invocation URLs, App Clip Codes, NFC, QR codes, Safari banners, Maps, Messages, target setup, App Store Connect experiences, size/capability constraints, NSUserActivity routing, SKOverlay promotion, App Group/keychain handoff, ephemeral notifications, location confirmation, and full-app migration. Use when creating App Clips or wiring App Clip invocation, experience configuration, or full-app handoff.

app-intentsSkill

Implement App Intents for Siri, Shortcuts, Spotlight, widgets, Control Center, and Apple Intelligence on iOS. Covers AppIntent actions, AppEntity and EntityQuery models, AppShortcutsProvider phrases, IndexedEntity Spotlight indexing, WidgetConfigurationIntent, SnippetIntent, and assistant schemas. Use when exposing app actions or entities to system surfaces.

app-store-optimizationSkill

Optimize App Store product pages for search visibility and conversion. Use for App Store Optimization (ASO), keyword research, app name/subtitle/keyword-field strategy, conversion-focused descriptions and promotional text, screenshot captions and ordering, Custom Product Pages with assigned search keywords, In-App Events, Product Page Optimization tests, localized metadata, ratings/review strategy, and in-app review prompt timing with RequestReviewAction or AppStore.requestReview. Also use when routing ASO vs App Store review, privacy/ATT, or StoreKit implementation boundaries.

app-store-reviewSkill

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.