---
title: "Announcing out-of-memory reporting in iOS"
metaTitle: "Announcing out-of-memory reporting in iOS | bitdrift"
slug: "oom-reporting-ios"
blurb: "In bitdrift for iOS, OOM terminations now appear in the Session Timeline and Issues page with a stack trace and the full session context leading up to the crash."
metaDescription: "In bitdrift for iOS, OOM terminations now appear in the Session Timeline and Issues page with stack trace and full session context leading up to the crash."
cover:
  url: "/assets/posts/oom-reporting-ios/feature-oom-timeline-desktop@1x.webp"
  alt: "Session Timeline showing out-of-memory diagnostics in bitdrift for iOS."
socialThumbnail:
  url: "/assets/posts/oom-reporting-ios/feature-oom-timeline-desktop@1x.webp"
  alt: "Session Timeline showing out-of-memory diagnostics in bitdrift for iOS."
author:
  - "ari"
tags:
  - "mobile"
  - "observability"
  - "feature"
publishedDate: "2026-09-22T12:00:00Z"
modifiedDate: "2026-09-22T12:00:00Z"

---

For years, keeping a healthy iOS app meant understanding and dealing with crashes and any kind of unexpected exits. There are many tools for investigating different types of failures, like in-process crash reporters, MetricKit Diagnostics, Xcode Organizer, Testflight reports, and so on. However, there’s a major blind spot when investigating failures in the wild: out-of-memory terminations (OOMs).

Apple provides two sources of OOM information:

* Metrics with MetricKit help to understand how many of your app kills were caused by an actual OOM.
* Reports are available whenever you can reproduce the issue locally with your device.

But there hasn't been a first-class source of production OOM diagnostics. Until now, we’ve had to rely on heuristics.

Today, the bitdrift Capture SDK turns iOS 27's new MemoryExceptionDiagnostic into an actionable issue: OOM terminations now appear in the Session Timeline and Issues page with their stack trace and the full session context leading up to the crash. The feature requires apps built with Xcode 27 running on iOS 27 or later, but existing customers can adopt the new SDK release without upgrading their toolchain.

## A new MetricKit and the missing diagnostic

At [WWDC26](https://developer.apple.com/wwdc26/), Apple announced a new Swift-first MetricKit API that’s going to be available in iOS, iPadOS, Mac Catalyst, macOS, and visionOS 27. It’s a bit more than a simple update or a port to the Swift language. The framework has been redesigned to provide typed reports (no more dealing with dictionaries!), using the new Swift concurrency model with async sequences, [`StateReporting`](https://developer.apple.com/documentation/statereporting?language=objc), and some other improvements.

But, for teams focused on app health, there’s one addition that stands out: [`MemoryExceptionDiagnostic`](https://developer.apple.com/documentation/metrickit/memoryexceptiondiagnostic?language=objc). It’s the first first-party diagnostic Apple has offered for a problem teams have historically had to guess at.

Historically, teams have tried to infer out-of-memory terminations [through a variety of heuristics](https://engineering.fb.com/2015/08/24/ios/reducing-fooms-in-the-facebook-ios-app/), like observing and persisting how much memory an app is using over time, detecting memory warnings, identifying typical resource-intensive components (e.g. camera, maps), and correlating all of that with unexpected exits.

These heuristics exist because Jetsam (whose name clearly explains that it jettisons processes) terminates an app the moment it exceeds its memory budget, without leaving anything behind to describe the event and without sending any signal that the app can catch itself before or after termination. However, there are other reasons an app can exit, which is why those heuristics are imperfect: they can generate false positives or negatives.

Happily, the new MetricKit API changes that: when Jetsam terminates an app for exceeding its memory limit, MetricKit is going to deliver a new `MemoryExceptionDiagnostic` which includes an associated stack trace. This is really good for teams because they can now confidently measure the real impact of OOMs in their app’s health. However, understanding something happened is not the same as understanding why it happened: many OOMs are still going to be somewhat difficult to deal with. The diagnostic doesn’t provide something more actionable like a heap dump and it’s far away from being a [Jetsam Event Report](https://developer.apple.com/documentation/xcode/identifying-high-memory-use-with-jetsam-event-reports). So, many times, the report cannot identify every object retained by the app, explain which allocations grew over time, or prove a single root cause from the stack alone.

Even with those limitations, replacing heuristics with a first-party diagnostic is a meaningful step forward in addressing OOMs. It turns something that was extremely opaque into an actionable signal. And even though stack traces aren’t the best way to deal with OOMs, they can help in many scenarios.

## From a memory exception to a debuggable issue

One of bitdrift's advantages is that it provides you with the context surrounding an event. When a session ends with an OOM, teams can investigate all the telemetry leading up to that termination rather than just staring at an isolated stack trace: they can see [how memory changes over time](https://docs.bitdrift.io/product/timeline/resource-utilization.html), all the logs that happened throughout the session, and so on. That’s why we decided to integrate this new MetricKit as soon as it became available and even release it while it was part of the beta.

Starting with `Capture-ios v0.25.0`, *apps that integrate the SDK through Swift Package Manager, build with Xcode 27, and run on iOS 27 or later* can see OOM terminations in the Session Timeline and Issues pages. The issue is reported as an `EXC_RESOURCE` and shown in the session timeline as `Fatal Issue: Out of Memory`. It also includes the stack trace associated with the diagnostic plus all the relevant environmental data.

Here’s an example of how it looks:

<Image
    alt="Session Timeline showing memory spikes, a memory warning, and a fatal out-of-memory issue."
    asset="/assets/posts/oom-reporting-ios/oom-timeline@1x.webp"
    large
/>

As can be seen in the image, the memory chart shows two big spikes, each one corresponding to a different OOM. The vertical marker on the first spike points to the event currently selected in the timeline that is the `Fatal Issue: Out Of Memory`. Before the termination, [our SDK](https://bitdrift.io/feature/performance-centric) also detected and reported the `Memory Warning` (usage of 2.77 GB out of 3.46 GB).

The image below shows what we can see once we navigate to `View Issue` (or find the issue directly under the `Issues` page) and all the information associated with the crash. In this particular case, the stack trace points to the code that is constantly creating arrays until the process runs out of memory:

<Image
    alt="Issues page showing an out-of-memory issue grouped as EXC_RESOURCE with stack trace details."
    asset="/assets/posts/oom-reporting-ios/oom-issue@1x.webp"
    large
/>

**Why express this as `EXC_RESOURCE`?** First of all, [Apple defines `EXC_RESOURCE`](https://developer.apple.com/documentation/xcode/exc_resource) as the Mach Exception raised when a process exceeds its resource consumption limit:

*The operating system stopped the process because the process exceeded a limit on resource consumption, like CPU time or **memory**.*

And even though MetricKit does not expose a Mach Exception code with this diagnostic, to validate our classification we reproduced the same OOM while using the new [`CrashReporterExtension`](https://developer.apple.com/documentation/crashreportextension/crashreporterextension). In the report, the [`CrashReason`](https://developer.apple.com/documentation/crashreportextension/crashreason) has the value `11` in the [`exception`](https://developer.apple.com/documentation/crashreportextension/crashreason/exception) property which maps to `EXC_RESOURCE` in the `mach/exception_types.h` header file:

<Image
    alt="Crash reason output showing exception code 11, which maps to EXC_RESOURCE."
    asset="/assets/posts/oom-reporting-ios/exc-resource-validation@1x.webp"
    large
/>

## Shipping iOS 27 support without leaving older toolchains behind

As can be seen, it’s really important for us to have this feature in our product and help users address OOMs. However, integrating the new API introduced a new problem: distribution.

In general, an `@available(iOS 27, *)` check is enough to protect code at runtime, but `MetricManager` must be compiled by a toolchain that knows about it, which in this case is Xcode 27 that comes with Swift 6.4. If we only generated an `.xcframework` with Xcode 27, customers still building their apps with older Xcode versions wouldn’t be able to use that new version of the SDK.

To solve this problem, we generate two different `.xcframework` variants under the same public version of the Capture SDK. One is built with Xcode 27 and includes the iOS 27 MetricKit implementation. The other excludes that code at compile time using `#if compiler(>=6.4)`, which is the Swift version bundled with Xcode 27. This makes that `.xcframework` compatible with all previous Xcode versions.

We also wanted customers to receive the right variant without changing anything in their SDK setup. To do that, we take advantage of [a SPM feature](https://docs.swift.org/latest/documentation/packagemanagerdocs/swiftversionspecificpackaging/) that lets it select a manifest based on the Swift tools version supported by the current toolchain. This means that, in the `capture-ios` repository, we now expose two manifests:

- `Package.swift` declares Swift tools version 6.4 and uses the `.xcframework` with the iOS 27 MetricKit implementation.
- `Package@swift-6.0.swift` declares Swift tools version 6.0.0 and uses the variant without the new MetricKit.

SPM is capable of detecting the most suitable manifest for the current toolchain. So, customers using Xcode 27 receive the new MetricKit implementation, while customers on earlier toolchains continue to receive the existing MetricKit implementation. No one is forced to update Xcode just to adopt the new Capture release.

## Conclusion

OOM terminations have long been some of the most difficult iOS failures to track and address in production. With iOS 27’s `MemoryExceptionDiagnostic`, bitdrift can turn a previously opaque termination into an actionable issue with an associated stack trace and the whole context the Capture SDK preserved leading up to the Jetsam event. This is much more than just another crash type: it’s a reliable signal for a class of terminations that previously required inference, guesswork, or luck to investigate. With `capture-ios v0.25.0`, luck is no longer a debugging strategy.

---

## Frequently asked questions

### What is MemoryExceptionDiagnostic in iOS 27's MetricKit?

It’s a new MetricKit diagnostic Apple introduced in iOS 27 that includes a stack trace whenever Jetsam terminates an app for exceeding its memory limit, giving developers a first-party signal for out-of-memory crashes.

### Does MemoryExceptionDiagnostic eliminate the need for OOM heuristics?

For identifying Jetsam-driven OOM terminations, yes: it replaces guesswork with a first-party diagnostic. Teams still need broader session context to understand why the OOM happened, which is where the Capture timeline, logs, and resource data help.

### How does bitdrift's Capture SDK use MemoryExceptionDiagnostic?

Starting with `Capture-ios v0.25.0`, bitdrift's Capture SDK surfaces OOM terminations in the Session Timeline and Issues page, classified as `EXC_RESOURCE`, alongside the full session context including memory usage over time, logs, and the associated stack trace.

### What do I need to see OOM diagnostics in bitdrift?

For the full feature, your app needs to integrate `Capture-ios v0.25.0` through Swift Package Manager, build with Xcode 27, and run on iOS 27 or later.

### Do I need to upgrade Xcode to use the new Capture SDK release?

No. bitdrift ships two `.xcframework` variants under the same SDK version, and Swift Package Manager automatically selects the right one, so teams on older Xcode versions can still adopt the release without changing their existing setup.
