metrickit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMetricKit
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
immediate diagnostic payloads (crashes, hangs, disk-write exceptions) with
full call-stack trees for triage.
使用MetricKit从生产环境设备收集聚合的性能指标和崩溃诊断数据。该框架会输出每日指标负载(CPU、内存、启动时间、卡顿率、动画掉帧、网络使用情况)和实时诊断负载(崩溃、卡顿、磁盘写入异常),附带完整调用栈树用于问题排查。
Contents
目录
Subscriber Setup
订阅者设置
Register a subscriber as early as possible — ideally in
or . MetricKit
starts accumulating reports after the first access to .
application(_:didFinishLaunchingWithOptions:)App.initMXMetricManager.sharedswift
import MetricKit
final class MetricsSubscriber: NSObject, MXMetricManagerSubscriber {
static let shared = MetricsSubscriber()
func subscribe() {
MXMetricManager.shared.add(self)
}
func unsubscribe() {
MXMetricManager.shared.remove(self)
}
func didReceive(_ payloads: [MXMetricPayload]) {
// Handle daily metrics
}
func didReceive(_ payloads: [MXDiagnosticPayload]) {
// Handle diagnostics (crashes, hangs, disk writes)
}
}尽可能早地注册订阅者——理想位置是或者。MetricKit会在首次访问后开始积累报告。
application(_:didFinishLaunchingWithOptions:)App.initMXMetricManager.sharedswift
import MetricKit
final class MetricsSubscriber: NSObject, MXMetricManagerSubscriber {
static let shared = MetricsSubscriber()
func subscribe() {
MXMetricManager.shared.add(self)
}
func unsubscribe() {
MXMetricManager.shared.remove(self)
}
func didReceive(_ payloads: [MXMetricPayload]) {
// 处理每日指标
}
func didReceive(_ payloads: [MXDiagnosticPayload]) {
// 处理诊断数据(崩溃、卡顿、磁盘写入异常)
}
}UIKit Registration
UIKit注册方式
swift
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
MetricsSubscriber.shared.subscribe()
return true
}swift
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
MetricsSubscriber.shared.subscribe()
return true
}SwiftUI Registration
SwiftUI注册方式
swift
@main
struct MyApp: App {
init() {
MetricsSubscriber.shared.subscribe()
}
var body: some Scene {
WindowGroup { ContentView() }
}
}swift
@main
struct MyApp: App {
init() {
MetricsSubscriber.shared.subscribe()
}
var body: some Scene {
WindowGroup { ContentView() }
}
}Receiving Metric Payloads
接收指标负载
MXMetricPayloadswift
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)
processMetrics(payload)
}
}Availability: — iOS 13.0+, macOS 10.15+, visionOS 1.0+
MXMetricPayloadMXMetricPayloadswift
func didReceive(_ payloads: [MXMetricPayload]) {
for payload in payloads {
let begin = payload.timeStampBegin
let end = payload.timeStampEnd
let version = payload.latestApplicationVersion
// 处理前先持久化原始JSON
let jsonData = payload.jsonRepresentation()
persistPayload(jsonData, from: begin, to: end)
processMetrics(payload)
}
}可用性: — iOS 13.0+, macOS 10.15+, visionOS 1.0+
MXMetricPayloadReceiving Diagnostic Payloads
接收诊断负载
MXDiagnosticPayloadswift
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for payload in payloads {
let jsonData = payload.jsonRepresentation()
persistPayload(jsonData)
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 let launchDiags = payload.appLaunchDiagnostics {
for launchDiag in launchDiags {
handleSlowLaunch(launchDiag)
}
}
}
}Availability: — iOS 14.0+, macOS 12.0+, visionOS 1.0+
MXDiagnosticPayloadMXDiagnosticPayloadswift
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for payload in payloads {
let jsonData = payload.jsonRepresentation()
persistPayload(jsonData)
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 let launchDiags = payload.appLaunchDiagnostics {
for launchDiag in launchDiags {
handleSlowLaunch(launchDiag)
}
}
}
}可用性: — iOS 14.0+, macOS 12.0+, visionOS 1.0+
MXDiagnosticPayloadKey Metrics
核心指标
Launch Time — MXAppLaunchMetric
启动时间 — MXAppLaunchMetric
swift
if let launch = payload.applicationLaunchMetrics {
let firstDraw = launch.histogrammedTimeToFirstDraw
let optimized = launch.histogrammedOptimizedTimeToFirstDraw
let resume = launch.histogrammedApplicationResumeTime
let extended = launch.histogrammedExtendedLaunch
}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
运行时间 — 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
}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
CPU、内存和响应性
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>
}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
}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.
}swift
if let exits = payload.applicationExitMetrics {
let fg = exits.foregroundExitData
let bg = exits.backgroundExitData
// 可检查正常退出、异常退出、看门狗杀死、内存杀死等情况
}Call Stack Trees
调用栈树
MXCallStackTreejsonRepresentation()atosSee references/metrickit-patterns.md for crash/hang handling code and JSON structure details.
Availability: — iOS 14.0+, macOS 12.0+, visionOS 1.0+
MXCallStackTree每个诊断数据都会附带。使用提取帧数据,然后通过或者向分析服务上传dSYM文件进行符号解析。
MXCallStackTreejsonRepresentation()atos查看references/metrickit-patterns.md获取崩溃/卡顿处理代码和JSON结构详情。
可用性: — iOS 14.0+, macOS 12.0+, visionOS 1.0+
MXCallStackTreeCustom Signpost Metrics
自定义Signpost指标
Use with a MetricKit log handle to capture custom performance
intervals. These appear in the daily under .
mxSignpostMXMetricPayloadsignpostMetricsswift
let metricLog = MXMetricManager.makeLogHandle(category: "Networking")See references/metrickit-patterns.md for signpost emission patterns and reading custom metrics from payloads.
搭配MetricKit日志句柄使用来捕获自定义性能区间。这些数据会出现在每日的字段下。
mxSignpostMXMetricPayloadsignpostMetricsswift
let metricLog = MXMetricManager.makeLogHandle(category: "Networking")查看references/metrickit-patterns.md获取signpost上报模式和从负载中读取自定义指标的方法。
Exporting and Uploading Payloads
导出与上传负载
Both payload types provide for serialization. Always persist raw JSON to disk before processing — the system delivers each payload once. Use and on launch to recover missed deliveries.
jsonRepresentation()pastPayloadspastDiagnosticPayloadsSee references/metrickit-patterns.md for export code and past payload retrieval.
两种负载类型都提供方法用于序列化。处理前务必先将原始JSON持久化到磁盘——系统仅会推送一次每个负载。启动时可以通过和恢复遗漏的推送数据。
jsonRepresentation()pastPayloadspastDiagnosticPayloads查看references/metrickit-patterns.md获取导出代码和历史负载获取方法。
Extended Launch Measurement
扩展启动测量
Track post-first-draw setup work as part of the launch metric:
swift
let taskID = MXLaunchTaskID("com.example.app.loadDatabase")
MXMetricManager.shared.extendLaunchMeasurement(forTaskID: taskID)
await database.load()
MXMetricManager.shared.finishExtendedLaunchMeasurement(forTaskID: taskID)Extended launch times appear under in .
histogrammedExtendedLaunchMXAppLaunchMetric可以将首帧绘制后的初始化工作纳入启动指标统计:
swift
let taskID = MXLaunchTaskID("com.example.app.loadDatabase")
MXMetricManager.shared.extendLaunchMeasurement(forTaskID: taskID)
await database.load()
MXMetricManager.shared.finishExtendedLaunchMeasurement(forTaskID: taskID)扩展启动时间会展示在的字段下。
MXAppLaunchMetrichistogrammedExtendedLaunchXcode Organizer Integration
Xcode Organizer集成
Xcode Organizer shows aggregated MetricKit data across opted-in users. Use it for trend analysis alongside on-device collection routed to your own backend.
See references/metrickit-patterns.md for Organizer tab details.
Xcode Organizer会展示已授权用户的聚合MetricKit数据。你可以结合上传到自有后端的设备端数据,用它做趋势分析。
查看references/metrickit-patterns.md获取Organizer标签页详情。
Common Mistakes
常见错误
DON'T: Subscribe to MXMetricManager too late
不要:太晚订阅MXMetricManager
The system may deliver pending payloads shortly after launch. Subscribing
late (e.g., in a view controller) risks missing them entirely.
swift
// WRONG — subscribing in a view controller
override func viewDidLoad() {
super.viewDidLoad()
MXMetricManager.shared.add(self)
}
// CORRECT — subscribe in application(_:didFinishLaunchingWithOptions:)
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions opts: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
MXMetricManager.shared.add(metricsSubscriber)
return true
}系统可能会在启动后短时间内推送待处理的负载。如果订阅太晚(比如在ViewController中订阅),可能会完全错过这些数据。
swift
// 错误 —— 在ViewController中订阅
override func viewDidLoad() {
super.viewDidLoad()
MXMetricManager.shared.add(self)
}
// 正确 —— 在application(_:didFinishLaunchingWithOptions:)中订阅
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions opts: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
MXMetricManager.shared.add(metricsSubscriber)
return true
}DON'T: Ignore MXDiagnosticPayload
不要:忽略MXDiagnosticPayload
Only handling means you miss crash, hang, and disk-write
diagnostics — the most actionable data MetricKit provides.
MXMetricPayloadswift
// WRONG — only implementing metric callback
func didReceive(_ payloads: [MXMetricPayload]) { /* ... */ }
// CORRECT — implement both callbacks
func didReceive(_ payloads: [MXMetricPayload]) { /* ... */ }
func didReceive(_ payloads: [MXDiagnosticPayload]) { /* ... */ }仅处理意味着你会错过崩溃、卡顿和磁盘写入诊断——这些是MetricKit提供的最具可操作性的数据。
MXMetricPayloadswift
// 错误 —— 仅实现指标回调
func didReceive(_ payloads: [MXMetricPayload]) { /* ... */ }
// 正确 —— 实现两个回调
func didReceive(_ payloads: [MXMetricPayload]) { /* ... */ }
func didReceive(_ payloads: [MXDiagnosticPayload]) { /* ... */ }DON'T: Process payloads without persisting first
不要:处理负载前不先持久化
The system delivers each payload once. If your subscriber crashes during
processing, the data is lost permanently.
swift
// WRONG — process inline, crash loses data
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for p in payloads {
riskyProcessing(p) // If this crashes, payload is gone
}
}
// CORRECT — persist raw JSON first, then process
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for p in payloads {
let json = p.jsonRepresentation()
try? json.write(to: localCacheURL()) // Safe on disk
Task.detached { self.processAsync(json) }
}
}系统仅会推送一次每个负载。如果你的订阅者在处理过程中崩溃,数据会永久丢失。
swift
// 错误 —— 直接处理,崩溃会丢失数据
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for p in payloads {
riskyProcessing(p) // 如果这里崩溃,负载就丢失了
}
}
// 正确 —— 先持久化原始JSON,再处理
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for p in payloads {
let json = p.jsonRepresentation()
try? json.write(to: localCacheURL()) // 安全存储到磁盘
Task.detached { self.processAsync(json) }
}
}DON'T: Do heavy work synchronously in didReceive
不要:在didReceive中同步执行重负载任务
The callback runs on an arbitrary thread. Blocking it with heavy processing
or synchronous network calls delays delivery of subsequent payloads.
swift
// WRONG — synchronous upload in callback
func didReceive(_ payloads: [MXMetricPayload]) {
for p in payloads {
let data = p.jsonRepresentation()
URLSession.shared.uploadTask(with: request, from: data).resume() // sync wait
}
}
// CORRECT — persist and dispatch async
func didReceive(_ payloads: [MXMetricPayload]) {
for p in payloads {
let json = p.jsonRepresentation()
persistLocally(json)
Task.detached(priority: .utility) {
await self.uploadToBackend(json)
}
}
}回调运行在任意线程上。如果用重处理操作或者同步网络请求阻塞该线程,会延迟后续负载的送达。
swift
// 错误 —— 回调中同步上传
func didReceive(_ payloads: [MXMetricPayload]) {
for p in payloads {
let data = p.jsonRepresentation()
URLSession.shared.uploadTask(with: request, from: data).resume() // 同步等待
}
}
// 正确 —— 持久化后异步调度
func didReceive(_ payloads: [MXMetricPayload]) {
for p in payloads {
let json = p.jsonRepresentation()
persistLocally(json)
Task.detached(priority: .utility) {
await self.uploadToBackend(json)
}
}
}DON'T: Expect immediate data in development
不要:在开发环境期望立即获得数据
MetricKit aggregates data over 24-hour windows. Payloads do not arrive
immediately after instrumenting. Use Xcode Organizer or simulated payloads
for faster iteration during development.
MetricKit以24小时为窗口聚合数据。埋点完成后负载不会立即送达。开发过程中可以使用Xcode Organizer或者模拟负载来加快迭代速度。
Review Checklist
检查清单
- called in
MXMetricManager.shared.add(subscriber)orapplication(_:didFinishLaunchingWithOptions:)App.init - Subscriber conforms to and inherits
MXMetricManagerSubscriberNSObject - Both and
didReceive(_: [MXMetricPayload])implementeddidReceive(_: [MXDiagnosticPayload]) - Raw persisted to disk before processing
jsonRepresentation() - Heavy processing dispatched asynchronously off the callback thread
- JSON uploaded with dSYMs for symbolication
MXCallStackTree - Custom signpost metrics limited to critical code paths
- and
pastPayloadschecked on launch for missed deliveriespastDiagnosticPayloads - Extended launch tasks call both and
extendLaunchMeasurementfinishExtendedLaunchMeasurement - Analytics backend accepts and stores MetricKit JSON format
- Xcode Organizer reviewed for regression trends alongside on-device data
- 在
MXMetricManager.shared.add(subscriber)或者application(_:didFinishLaunchingWithOptions:)中调用App.init - 订阅者遵循协议并继承
MXMetricManagerSubscriberNSObject - 同时实现了和
didReceive(_: [MXMetricPayload])didReceive(_: [MXDiagnosticPayload]) - 处理前先将原始持久化到磁盘
jsonRepresentation() - 重处理操作在回调线程外异步调度
- JSON和dSYM一起上传用于符号解析
MXCallStackTree - 自定义signpost指标仅用于关键代码路径
- 启动时检查和
pastPayloads恢复遗漏的推送pastDiagnosticPayloads - 扩展启动任务同时调用了和
extendLaunchMeasurementfinishExtendedLaunchMeasurement - 分析后端支持接收并存储MetricKit JSON格式
- 结合Xcode Organizer和设备端数据排查回归趋势
References
参考资料
- Extended patterns: references/metrickit-patterns.md
- MetricKit framework
- MXMetricManager
- MXMetricManagerSubscriber
- MXMetricPayload
- MXDiagnosticPayload
- 扩展模式: references/metrickit-patterns.md
- MetricKit framework
- MXMetricManager
- MXMetricManagerSubscriber
- MXMetricPayload
- MXDiagnosticPayload