Skip to main content

Legacy Memory Telemetry with MetricKit

swift

Collect memory and termination metrics on versions of iOS before the MetricManager async API.

Use MXMetricManagerSubscriber when supporting versions of iOS before iOS 27. Memory usage is available from iOS 13, while the termination counters are collected on iOS 14 and later. Create one telemetry object, register it with the shared manager, and call stop() when you no longer want reports. Keeping your own reference makes that lifecycle explicit.

import Foundation
import MetricKit

final class LegacyMemoryTelemetry: NSObject, MXMetricManagerSubscriber {
    typealias MemoryUpload = (
        _ name: String,
        _ bytes: Double
    ) -> Void

    typealias CountUpload = (
        _ name: String,
        _ count: Int
    ) -> Void

    private let uploadMemory: MemoryUpload
    private let uploadCount: CountUpload

    init(
        uploadMemory: @escaping MemoryUpload,
        uploadCount: @escaping CountUpload
    ) {
        self.uploadMemory = uploadMemory
        self.uploadCount = uploadCount
    }

    func start() {
        MXMetricManager.shared.add(self)
    }

    func stop() {
        MXMetricManager.shared.remove(self)
    }

    func didReceive(_ payloads: [MXMetricPayload]) {
        for payload in payloads {
            if let memory = payload.memoryMetrics {
                uploadMemory(
                    "peak_memory",
                    memory.peakMemoryUsage.converted(to: .bytes).value
                )
                uploadMemory(
                    "suspended_memory",
                    memory.averageSuspendedMemory.averageMeasurement
                        .converted(to: .bytes).value
                )
            }

            if #available(iOS 14.0, *),
               let exits = payload.applicationExitMetrics {
                uploadCount(
                    "fg_memory_limit",
                    exits.foregroundExitData
                        .cumulativeMemoryResourceLimitExitCount
                )
                uploadCount(
                    "fg_watchdog",
                    exits.foregroundExitData
                        .cumulativeAppWatchdogExitCount
                )
                uploadCount(
                    "bg_memory_limit",
                    exits.backgroundExitData
                        .cumulativeMemoryResourceLimitExitCount
                )
                uploadCount(
                    "bg_system_pressure",
                    exits.backgroundExitData
                        .cumulativeMemoryPressureExitCount
                )
            }
        }
    }

    @available(iOS 14.0, *)
    func didReceive(_ payloads: [MXDiagnosticPayload]) {}
}

Create and retain one instance, then register it with the shared manager:

let telemetry = LegacyMemoryTelemetry(
    uploadMemory: { name, bytes in
        analytics.upload(name, value: bytes)
    },
    uploadCount: { name, count in
        analytics.upload(name, value: count)
    }
)

telemetry.start()

Call stop() before releasing the instance. MetricKit normally delivers aggregated payloads about once per day. During development, Xcode can send a sample payload from Debug > Simulate MetricKit Payloads.