metrickit について
MetricKit で本番環境のテレメトリーを収集・分析する開発者向けガイドです。最新のレポートシーケンス、永続化、診断、カスタム指標、iOS 26 互換経路を扱います。
主な機能
- 01メトリックと診断レポートのシーケンス
- 02レポートの永続化と後続アップロード
- 03クラッシュ、ハング、リソース診断
- 04カスタム signpost 区間指標
- 05起動タスクの拡張計測
- 06iOS 26 互換性の案内
活用シーン
- 01本番環境のパフォーマンスレポート収集
- 02分析やアップロード前のレポート保存
- 03クラッシュ、ハング、リソース問題の調査
- 04起動に重要なアプリタスクの計測
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill metrickitインストール後、Claude Code で/metrickitを入力して読み込まれたか確認します。
同梱ファイルevals/evals.jsonreferences/metrickit-patterns.md
詳細
MetricKit
MetricKit を使用すると、ローカルの Instruments および Xcode Organizer による分析を補完する、オーバーヘッドの小さい本番環境向けテレメトリーを利用できます。iOS および iPadOS 27 では、Swift を優先する MetricManager のレポートシーケンスを使用してください。MXMetricManager は、明示的な iOS 26 互換性ブランチでのみ使用してください。
ベータ版に関する注意: 以下の iOS/iPadOS 27 の API は、Apple の現行ベータ版ドキュメントに基づいています。この環境では Xcode 27 を利用できないため、ローカルでのコンパイラー検証は行っていません。リリース前に、リンク先の Apple ドキュメントを再確認し、リリース版の Xcode 27 SDK でコンパイルしてください。
永続的なデータ取り込み、詳細なレポート分析、または iOS 26 互換パスを実装する際は、MetricKit の拡張パターンと互換性パターンを読み込んでください。
目次
- MetricManager のセットアップ
- メトリクスレポートの受信
- 診断レポートの受信
- 主なメトリクス結果
- コールスタックツリー
- カスタムサインポストメトリクス
- 永続的なエクスポートとアップロード
- 起動計測の拡張
- iOS 26 互換性
- Xcode Organizer
- 適用範囲の境界
- よくある間違い
- レビュー用チェックリスト
- 参考資料
MetricManager のセットアップ
アプリの起動時に、長期間保持する MetricManager を 1 つ作成します。metricReports 用と diagnosticReports 用のコンシューマータスクを、それぞれ 1 つだけ開始してください。
どちらのプロパティも、エラーをスローしない AsyncSequence 値を公開します。
metricReports: some AsyncSequence<MetricReport, Never>diagnosticReports: some AsyncSequence<DiagnosticReport, Never>
Apple のドキュメントによると、1 つのシーケンスを複数のコンシューマーが同時に処理すると、受け取る内容が予測できない形で分割される可能性があります。単一のコンシューマーがレポートを受信して永続的に保存した後にのみ、各処理へ振り分けてください。購読開始が遅れると、レポートを取りこぼす可能性があります。
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()
}
}
永続化クロージャーの実装は、アプリケーションごとに異なります。レポートを破棄したり、ログに記録するだけにしたり、各レポートを直接アップロードしたりするのではなく、以下の永続化優先ワークフローに沿って実装してください。状態に関連付けられたメトリクスが必要な場合は、ドキュメントに記載されている init(enabledStateReportingDomains:) イニシャライザーと必須ドメインを指定して、マネージャーを作成してください。
メトリクスレポートの受信
MetricReport は Codable および Sendable に準拠しています。以下を通じて、ある期間の情報を記述します。
timeRange: DateInterval- 任意の
environmentメタデータ - 1 日全体および短い間隔の計測値を含む
intervalEntries - アプリケーションの状態に関連付けられた計測値を含む
stateEntries
メトリクスレポートは通常、日次で届きます。個別の結果を抽出する前に、レポート全体を永続的に保存してください。
日次分析では、ドキュメントに記載されている fullDayEntry を読み取り、その MetricResult 値に応じて処理を切り替えます。
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)
}
}
ベータ版または将来の結果によってデータ取り込みパイプラインが壊れないよう、@unknown default を使用してください。現在のアプリが結果を認識できない場合でも、エンコードされたレポートの生データを保持してください。
診断レポートの受信
DiagnosticReport は Codable および Sendable に準拠しています。timeRange、必須の environment メタデータ、および 1 つの DiagnosticResult を含みます。
診断は個別のイベントベースのレポートで、MetricKit が生成した際にプロンプトとして配信されることを想定しています。クラッシュ、ハング、リソースイベントのすべてでレポートが生成されるとは限りません。システムのサンプリングと適格性の条件が引き続き適用されます。
永続ストレージへの保存後、結果を明示的に振り分けます。
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)
}
iOS/iPadOS 27 の診断タイプは、CrashDiagnostic、HangDiagnostic、CPUExceptionDiagnostic、DiskWriteExceptionDiagnostic、AppLaunchDiagnostic、MemoryExceptionDiagnostic です。メモリ例外のケースは iOS 27 で新たに追加されました。
主なフィールドは次のとおりです。
| 診断 | 重要なフィールド |
|---|---|
CrashDiagnostic | callStackTree、例外のタイプ/コード/理由、シグナル、仮想メモリ領域、終了カテゴリ/理由 |
HangDiagnostic | callStackTree、hangDuration |
CPUExceptionDiagnostic | callStackTree、totalCPUTime、totalSampledTime |
DiskWriteExceptionDiagnostic | callStackTree、totalBytesWritten |
AppLaunchDiagnostic | callStackTree、launchDuration |
MemoryExceptionDiagnostic | callStackTree |
主要なメトリック結果
すべての結果をエクスポートするのではなく、調査内容に合った少数のテレメトリ用語を選びます。関連するカテゴリを優先します。たとえば、応答性と終了、ランタイム、CPU、メモリ、ネットワーク、ストレージ、または起動、ディスプレイ/GPU、カスタム区間です。
アプリのバージョンと環境メタデータで集計し、単一の値ではなく分布を比較して、リリースとの回帰の関連性を調べます。日次集計を、あるユーザー操作の正確なトレースとして扱わないでください。
MetricResult の各ケースをパーサーやダッシュボードにマッピングする際は、主要なメトリック結果のカタログを読み込んでください。
コールスタックツリー
iOS 27 の CallStackTree は MXCallStackTree に代わるものです。Codable および Sendable に準拠し、次の要素を公開します。
- フレームを走査する
forEachFrame - スレッド指向の分析に使う
callStackThreads - イメージとバイナリのメタデータを取得する
binaryInfo
完全な診断レポートを保存してから、フラット化やシンボリケーションを行ってください。サーバー側のシンボリケーションで対応するアーカイブと dSYM を使用できるよう、バイナリ識別子とオフセットを保持してください。
カスタムサインポストメトリック
マネージャーを介して OS ログを作成し、mxSignpost を使って計測を開始/終了します。
let log = manager.logHandle(category: "ImagePipeline")
mxSignpost(.begin, log: log, name: "Decode")
await decodeImage()
mxSignpost(.end, log: log, name: "Decode")
MetricKit は集計結果を MetricResult.signpostInterval として公開します。MetricReport に signpostMetrics 配列があるものとして検索しないでください。
MetricKit によるリソース計測が必要な場合は、mxSignpost を使用してください。Apple のドキュメントによると、OSSignposter と MetricKit のログハンドルで作成した区間では、mxSignpost が提供するリソース計測フィールドは設定されません。
大量のローカルトレースは、MetricKit の集計に使用する少数の安定した本番環境向け区間とは分けてください。
永続エクスポートとアップロード
レポートの配信は、少なくとも 1 回は取り込まれることを前提とした問題として扱います。
JSONEncoderで完全なMetricReportまたはDiagnosticReportをエンコードします。- アプリ所有の永続アウトボックスに、バイト列をアトミックにキューイングします。
- レポートのタイプ、スキーマのバージョン、アプリのバージョン、および安定した重複排除キーを記録します。
- キューイングが成功した後にのみ、ローカルでの処理を確認します。
- 再試行、バックオフ、バッチ処理、保持期間の制限を設けて、後でアップロードします。
- サーバーが受け付けた後にのみ、アウトボックス項目をアップロード済みとしてマークします。
let data = try JSONEncoder().encode(report)
try await durableOutbox.enqueue(data, kind: .metric)
durableOutbox はアプリ所有の抽象化であり、MetricKit API ではありません。シーケンスコンシューマー内で同期的なネットワークアップロードを行わないでください。
永続ローカルストレージは、最新のシーケンスを通じて受信したレポートを復旧するための仕組みです。最新のバックフィル API があると想定して、取り込みの成功をそれに依存させないでください。
起動後の計測の拡張
初回描画までの時間を超える処理には、マネージャーの trackLaunchTask(id:onTrackingError:_:) を使用します。
await manager.trackLaunchTask(
id: "bootstrap-data",
onTrackingError: { error in
recordLaunchTrackingError(error)
}
) {
await bootstrapApplication()
}
この API は @MainActor で、同期版と非同期版のオーバーロードがあります。タスククロージャーの戻り値とスローされたエラーは呼び出し元に伝播します。MetricManager.LaunchTaskError は、追跡中の処理を中断することなく onTrackingError を通じて報告されます。結果は MetricResult.extendedLaunch に表示されます。
安定した LaunchTaskID の値を使用し、起動に不可欠な処理のみを追跡してください。
iOS 26 との互換性
iOS 26 へのデプロイも引き続きサポートするアプリでは、利用可能性の境界を設けます。
- iOS/iPadOS 27:
MetricManagerを使用し、両方の非同期レポートシーケンスを処理します。 - iOS/iPadOS 26 およびそれ以前のサポート対象リリース:
MXMetricManager.shared、MXMetricManagerSubscriber、従来のペイロードコールバックを使用します。
pastPayloads と pastDiagnosticPayloads が利用できることが文書化されているのは、従来の API のみです。iOS 27 では MXMetricManager は非推奨のため、従来のペイロードを最新のパイプラインに混在させず、利用可能性チェックの内側に分離してください。
完全なサブスクライバー、従来のサインポスト、過去のペイロード、起動後の計測拡張のパターンについては、iOS 26 との互換性を参照してください。
Xcode Organizer
カスタムバックエンドを構築する前に、Xcode Organizer を使って、Apple が集約した本番環境のメトリクス、ハング、クラッシュ、リグレッションを調査してください。カスタムの相関分析、保持、アラート、既存のオブザーバビリティシステムとの統合が必要な場合は、MetricKit から直接データを取り込んでください。
開発用デバイスでの実行で、本番環境のレポートと同じ母集団、頻度、集約結果が再現されるとは限りません。
対象範囲の境界
| タスク | 代わりに使用 |
|---|---|
| 問題をローカルで再現する、または詳細なトレースを記録する | debugging-instruments |
| 保持されたオブジェクトやメモリグラフの経路を診断する | ios-memgraph-analysis |
| SwiftUI の無効化、ID、スクロールコードを調整する | swiftui-performance |
| EnergyKit の構造化された影響データを調べる | energykit |
| 一般的なロギングや os_signpost の方針を設計する | swift-logging |
MetricKit は、本番環境の症状や傾向を特定します。実際のコード修正は、影響を受けるサブシステムを担当するスキルに引き継いでください。
よくある間違い
| 間違い | 修正方法 |
|---|---|
| シーケンスのループ内で直接アップロードする | まずローカルでエンコードしてキューに入れ、後から非同期でアップロードします。 |
| すべての診断イベントでレポートが生成されると思い込む | 診断情報は、システムが生成するサンプリングされた証拠として扱います。 |
MetricManager に pastPayloads があると思い込む | 過去データの取り込みは、iOS 26 の MXMetricManager 分岐にのみ残します。 |
| 既知の列挙ケースのみを解析する | 生のレポートを保持し、@unknown default を使用します。 |
iOS 27 の分岐で MXMetricManager.makeLogHandle を使用する | manager.logHandle(category:) を使用します。 |
| iOS 27 の分岐で、起動タスクの開始と終了を別々に呼び出す | trackLaunchTask を使用します。 |
| MetricKit の集約結果をローカルトレースとして扱う | Instruments とサインポストを使って再現します。 |
確認チェックリスト
- iOS 27 の分岐は起動時に開始し、保持された
MetricManagerを 1 つ使用し、各レポートシーケンスにコンシューマーを 1 つ用意します。 - すべてのレポートは、分析やアップロードの前にエンコードされ、永続キューに格納されます。
- 診断情報とメトリクスの switch には
@unknown defaultを使用します。 - 不明な生のレポートを復元できる状態で保持します。
- シンボル化メタデータと対応する dSYM を保持します。
- カスタムの間隔には
manager.logHandle(category:)とmxSignpostを使用します。 - 起動後の拡張処理には
trackLaunchTaskを使用します。 - iOS 26 の
MXMetricManagerの経路は、利用可能性チェックの内側に分離します。 - 必要に応じて、本番環境の集約結果をローカルの Instruments 調査につなげます。
- iOS 27 ベータ版 API を再確認し、リリース版の Xcode 27 SDK でコンパイルします。