iOS 27 rebuilds MetricKit from the ground up with a modern, Swift-first API that delivers metric and diagnostic reports via async streams, adds Metal frame rate metrics, memory exception diagnostics, and crash termination categories, plus a new StateReporting framework to contextualize metrics by app state.
โข Entire public API rebuilt as a Swift-first async stream interface replacing the MXMetricManagerSubscriber delegate pattern
โข MetricReports and DiagnosticReports are now Codable out of the box, enabling direct JSON serialization
โข New Metal frame rate metric group added for game performance monitoring
โข New StateReporting framework introduced so metrics can be sliced by developer-defined app states (e.g. active tab, feature flag); crash diagnostics gain a termination category field
โข Async stream-based API replaces delegate callbacks, making it trivial to await metric and diagnostic reports at app launch without missing data
โข New StateReporting framework lets you segment any metric (hangs, hitches, CPU) by custom app states like active tab or feature flag, turning blended averages into actionable per-screen insights
โข New Metal frame rate metric and memory exception diagnostics give game developers and all apps richer triage data; crash diagnostics now include a termination category so you can directly correlate metric trends with individual crash events
Subscribes to MetricKit's new async streams at app launch, printing peak memory per interval and logging crash diagnostics with their new termination category to the console.
โimport UIKit+import SwiftUIimport MetricKitโ// MARK: - Old delegate-based approach (pre-iOS 27)+// MARK: - Service (keep alive for app lifetime)โclass AppDelegate: UIResponder, UIApplicationDelegate, MXMetricManagerSubscriber {โ let metricManager = MXMetricManager.shared+actor MetricKitService {+ static let shared = MetricKitService()โ func application(_ application: UIApplication,โ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {โ metricManager.add(self)โ return true+ func startMonitoring() async {+ async let _ = receiveMetrics()+ async let _ = receiveDiagnostics()}โ // Called once per day with an array of MXMetricPayloadโ func didReceive(_ payloads: [MXMetricPayload]) {โ for payload in payloads {โ if let memory = payload.memoryMetrics {โ print("Peak memory: \(memory.peakMemoryUsage)")+ private func receiveMetrics() async {+ let manager = MetricManager.shared+ for await report in manager.metricReports {+ for entry in report.intervalEntries {+ let interval = entry.interval+ print("Metric window: \(interval.start) โ \(interval.end)")+ let memoryMetrics = entry.metrics.filter { $0.group == .memory }+ for metric in memoryMetrics {+ switch metric {+ case .peakMemory(let measurement):+ print(" Peak memory: \(measurement)")+ default:+ break+ }+ }}โ // Encode manuallyโ if let json = try? JSONEncoder().encode(payload.jsonRepresentation()) {โ print(String(data: json, encoding: .utf8) ?? "")โ }}}โ // Called immediately for crash/hang diagnosticsโ func didReceive(_ payloads: [MXDiagnosticPayload]) {โ for payload in payloads {โ if let crashes = payload.crashDiagnostics {โ for crash in crashes {โ print("Exception type: \(crash.exceptionType ?? 0)")โ print("Backtrace:\n\(crash.callStackTree)")โ // No termination category available pre-iOS 27+ private func receiveDiagnostics() async {+ let manager = MetricManager.shared+ for await report in manager.diagnosticReports {+ for diagnostic in report.diagnostics {+ switch diagnostic {+ case .crash(let crash):+ print("Crash reason: \(crash.reason)")+ print("Termination category: \(crash.terminationCategory)")+ if let frames = crash.backtrace?.frames {+ print("Top frame: \(frames.first?.symbol ?? "unknown")")+ }+ case .hang(let hang):+ print("Hang duration: \(hang.duration)")+ default:+ break}}โ if let hangs = payload.hangDiagnostics {โ for hang in hangs {โ print("Hang duration: \(hang.hangDuration)")+ }+ }+}++// MARK: - App Entry++@main+struct MetricKitDemoApp: App {+ var body: some Scene {+ WindowGroup {+ ContentView()+ .task {+ await MetricKitService.shared.startMonitoring()}โ }}}+}++// MARK: - Encode a full report to JSON for server upload++func encodeReport(_ report: MetricReport) throws -> Data {+ let encoder = JSONEncoder()+ encoder.outputFormatting = .prettyPrinted+ return try encoder.encode(report)+}++struct ContentView: View {+ var body: some View {+ Text("MetricKit monitoring active")+ .padding()+ }}
MetricKit has been rebuilt from the ground up in iOS 27 with a contextually rich, Swift-first API that delivers metrics and diagnostics as async streams, plus new capabilities like Metal frame rate metrics, memory exception diagnostics, and per-state metric breakdowns via the StateReporting framework.
iOS 26+ introduces a Deferred Start API for AVCaptureSession that postpones initialization of non-preview outputs (like photo and movie outputs) until after the first preview frame renders, dramatically cutting camera app launch times.
Instruments 27 introduces Run Comparisons โ a new mode that directly diffs two profiling traces side-by-side in a single document to calculate exact performance deltas โ alongside the new Top Functions analysis mode that merges scattered call-tree nodes by self-weight to instantly surface the costliest functions regardless of call hierarchy.
In-depth guide
iOS 26 โ iOS 27 Migration Guide โSubscribe to metricReports and diagnosticReports at app launch in a detached Task or dedicated service class โ delayed subscription risks missing delivered reports. MetricManager must be kept alive for the duration of the app session. The new Swift-first APIs are distinct from the old delegate-based MXMetricManagerSubscriber APIs; they are not interchangeable.
Reports are delivered once daily for metrics; diagnostic reports are delivered immediately on-device. No Apple Intelligence hardware required.
iOS/macOS 27 introduces the StateReporting API and look-back trace collection tools, letting developers annotate game state (levels, graphics settings, network) directly into Metal performance traces and retroactively collect hours of performance data without pre-instrumentation.