Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@
> make sure you follow our [migration guide](https://docs.sentry.io/platforms/react-native/migration/) first.
<!-- prettier-ignore-end -->
## Unreleased

### Features

- Add `enableMetricKit` option to enable the iOS MetricKit integration ([#6540](https://github.com/getsentry/sentry-react-native/pull/6540))

@alwx alwx Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


When enabled, the iOS SDK sends `MXDiskWriteExceptionDiagnostic`, `MXCPUExceptionDiagnostic` and `MXHangDiagnostic` reports to Sentry. Requires iOS 15 or later and is disabled by default. MetricKit hang diagnostics are reported by the operating system and are separate from the app hangs captured by `enableAppHangTracking`, so enabling both can result in the same hang being reported twice.

```js
Sentry.init({
dsn: '___DSN___',
enableMetricKit: true,
});
```

## 8.21.0

### Features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,50 @@ - (void)testStartCreateOptionsWithDictionaryAutoPerformanceTracingDisabled
@"Did not disable Auto Performance Tracing");
}

- (void)testStartCreateOptionsWithDictionaryMetricKitEnabled
{
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://[email protected]/123456",
@"enableMetricKit" : @YES,
};
SentryOptions *actualOptions =
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");
XCTAssertTrue(actualOptions.enableMetricKit, @"Did not enable MetricKit");
}

- (void)testStartCreateOptionsWithDictionaryMetricKitDisabled
{
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://[email protected]/123456",
@"enableMetricKit" : @NO,
};
SentryOptions *actualOptions =
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");
XCTAssertFalse(actualOptions.enableMetricKit, @"Did not disable MetricKit");
}

- (void)testStartCreateOptionsWithDictionaryMetricKitDefault
{
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://[email protected]/123456",
};
SentryOptions *actualOptions =
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");
XCTAssertFalse(actualOptions.enableMetricKit, @"MetricKit should be disabled by default");
}

- (void)testStartCreateOptionsWithDictionarySpotlightEnabled
{
NSError *error = nil;
Expand Down
18 changes: 18 additions & 0 deletions packages/core/src/js/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,24 @@ export interface BaseReactNativeOptions {
*/
appHangTimeoutInterval?: number;

/**
* Use this feature to enable the Sentry MetricKit integration.
*
* When enabled, the SDK sends `MXDiskWriteExceptionDiagnostic`, `MXCPUExceptionDiagnostic` and
* `MXHangDiagnostic` to Sentry. Requires iOS 15 or later, because only on these versions MetricKit
* delivers diagnostic reports immediately. On earlier versions this option has no effect.
*
* MetricKit hang diagnostics are reported by the operating system and are distinct from the app
* hangs captured by `enableAppHangTracking`. Enabling both can result in the same hang being
* reported twice, from two different sources.
*
* iOS only
*
* @default false
* @platform ios
*/
enableMetricKit?: boolean;

/**
* The max queue size for capping the number of envelopes waiting to be sent by Transport.
*/
Expand Down
35 changes: 35 additions & 0 deletions packages/core/test/wrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,41 @@ describe('Tests Native Wrapper', () => {
expect(NATIVE.enableNative).toBe(true);
});

test('passes enableMetricKit to the Native SDK when set', async () => {
await NATIVE.initNativeSdk({
dsn: VALID_DSN,
enableNative: true,
autoInitializeNativeSdk: true,
enableMetricKit: true,
devServerUrl: undefined,
defaultSidecarUrl: undefined,
mobileReplayOptions: undefined,
});

expect(RNSentry.initNativeSdk).toHaveBeenCalled();
// @ts-expect-error mock value
const initParameter = RNSentry.initNativeSdk.mock.calls[0][0];
expect(initParameter).toEqual(expect.objectContaining({ enableMetricKit: true }));
expect(NATIVE.enableNative).toBe(true);
});

test('does not pass enableMetricKit to the Native SDK when not set', async () => {
await NATIVE.initNativeSdk({
dsn: VALID_DSN,
enableNative: true,
autoInitializeNativeSdk: true,
devServerUrl: undefined,
defaultSidecarUrl: undefined,
mobileReplayOptions: undefined,
});

expect(RNSentry.initNativeSdk).toHaveBeenCalled();
// @ts-expect-error mock value
const initParameter = RNSentry.initNativeSdk.mock.calls[0][0];
expect(initParameter).not.toHaveProperty('enableMetricKit');
expect(NATIVE.enableNative).toBe(true);
});

test('does not initialize with autoInitializeNativeSdk: false', async () => {
NATIVE.enableNative = false;
debug.warn = jest.fn();
Expand Down
Loading