CtrlK
BlogDocsLog inGet started
Tessl Logo

dpearson2699/swift-ios-skills

Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.

71

Quality

89%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

metrickit-patterns.mdskills/metrickit/references/

MetricKit Extended Patterns

Overflow reference for the metrickit skill. Contains deeper payload analysis, export patterns, custom signpost metrics, and extended launch measurement.

Contents

  • Call Stack Trees
  • Custom Signpost Metrics
  • Exporting and Uploading Payloads
  • Past Payloads
  • Extended Launch Measurement
  • Xcode Organizer Integration

Call Stack Trees

MXCallStackTree is attached to each diagnostic (crash, hang, CPU exception, disk write, app launch). Use jsonRepresentation() to extract and symbolicate.

func handleCrash(_ crash: MXCrashDiagnostic) {
    let tree = crash.callStackTree
    let treeJSON = tree.jsonRepresentation()

    let exceptionType = crash.exceptionType
    let signal = crash.signal
    let reason = crash.terminationReason

    uploadDiagnostic(
        type: "crash",
        exceptionType: exceptionType,
        signal: signal,
        reason: reason,
        callStack: treeJSON
    )
}

func handleHang(_ hang: MXHangDiagnostic) {
    let tree = hang.callStackTree
    let duration = hang.hangDuration  // Measurement<UnitDuration>
    uploadDiagnostic(type: "hang", duration: duration, callStack: tree.jsonRepresentation())
}

The JSON structure contains an array of call stack frames with binary name, offset, and address. Symbolicate using atos or upload dSYMs to your analytics service.

Availability: MXCallStackTree — iOS 14.0+, macOS 12.0+, visionOS 1.0+

Custom Signpost Metrics

Use mxSignpost with a MetricKit log handle to capture custom performance intervals. These appear in the daily MXMetricPayload under signpostMetrics.

Creating a Log Handle

let metricLog = MXMetricManager.makeLogHandle(category: "Networking")

Emitting Signposts

import os

func fetchData() async throws -> Data {
    let signpostID = MXSignpostIntervalData.makeSignpostID(log: metricLog)

    mxSignpost(.begin, log: metricLog, name: "DataFetch", signpostID: signpostID)
    let data = try await URLSession.shared.data(from: url).0
    mxSignpost(.end, log: metricLog, name: "DataFetch", signpostID: signpostID)

    return data
}

Reading Custom Metrics from Payload

if let signposts = payload.signpostMetrics {
    for metric in signposts {
        let name = metric.signpostName       // "DataFetch"
        let category = metric.signpostCategory // "Networking"
        let count = metric.totalCount
        if let intervalData = metric.signpostIntervalData {
            let avgMemory = intervalData.averageMemory
            let cumulativeCPUTime = intervalData.cumulativeCPUTime
        }
    }
}

The system limits the number of custom signpost metrics per log to reduce on-device overhead. Reserve custom metrics for critical code paths.

Exporting and Uploading Payloads

Both payload types conform to NSSecureCoding and provide jsonRepresentation() for easy serialization.

func persistPayload(_ jsonData: Data, from: Date? = nil, to: Date? = nil) {
    let fileName = "metrics_\(ISO8601DateFormatter().string(from: Date())).json"
    let url = FileManager.default.temporaryDirectory.appending(path: fileName)
    try? jsonData.write(to: url)
}

func uploadPayloads(_ jsonData: Data) {
    Task.detached(priority: .utility) {
        var request = URLRequest(url: URL(string: "https://api.example.com/metrics")!)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = jsonData
        _ = try? await URLSession.shared.data(for: request)
    }
}

Past Payloads

If the subscriber was not registered when payloads arrived, retrieve them using pastPayloads and pastDiagnosticPayloads. These return reports generated since the last allocation of the shared manager.

let pastMetrics = MXMetricManager.shared.pastPayloads
let pastDiags = MXMetricManager.shared.pastDiagnosticPayloads

Extended Launch Measurement

Track post-first-draw setup work (loading databases, restoring state) as part of the launch metric using extended launch measurement.

let taskID = MXLaunchTaskID("com.example.app.loadDatabase")

MXMetricManager.shared.extendLaunchMeasurement(forTaskID: taskID)
// Perform extended launch work...
await database.load()
MXMetricManager.shared.finishExtendedLaunchMeasurement(forTaskID: taskID)

Extended launch times appear under histogrammedExtendedLaunch in MXAppLaunchMetric.

Xcode Organizer Integration

Xcode Organizer shows the same MetricKit data aggregated across all users who have opted in to share diagnostics. Use Organizer for trend analysis:

  • Metrics tab: Battery, performance, and disk-write metrics over time
  • Regressions tab: Automatic detection of metric regressions per version
  • Crashes tab: Crash logs with symbolicated stack traces

MetricKit on-device collection complements Organizer by letting you route raw data to your own backend for custom dashboards, alerting, and filtering by user cohort.

Apple Documentation Links

skills

CHANGELOG.md

README.md

tile.json