Skip to content

Commit e97c5b7

Browse files
committed
feat: Add experimental flag enableUnhandledCPPExceptionsV2 on iOS
1 parent 1a14c8b commit e97c5b7

4 files changed

Lines changed: 101 additions & 0 deletions

File tree

packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.mm

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,82 @@ - (void)testCreateOptionsWithDictionarySpotlightZero
239239
XCTAssertFalse(actualOptions.enableSpotlight, @"Did not disable spotlight");
240240
}
241241

242+
- (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Enabled
243+
{
244+
RNSentry *rnSentry = [[RNSentry alloc] init];
245+
NSError *error = nil;
246+
247+
NSDictionary *_Nonnull mockedReactNativeDictionary = @{
248+
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
249+
@"_experiments" : @ {
250+
@"enableUnhandledCPPExceptionsV2" : @YES,
251+
},
252+
};
253+
SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary
254+
error:&error];
255+
256+
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
257+
XCTAssertNil(error, @"Should not pass no error");
258+
259+
id experimentalOptions = [actualOptions valueForKey:@"experimental"];
260+
XCTAssertNotNil(experimentalOptions, @"Experimental options should not be nil");
261+
262+
BOOL enableUnhandledCPPExceptions =
263+
[[experimentalOptions valueForKey:@"enableUnhandledCPPExceptionsV2"] boolValue];
264+
XCTAssertTrue(
265+
enableUnhandledCPPExceptions, @"enableUnhandledCPPExceptionsV2 should be enabled");
266+
}
267+
268+
- (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Disabled
269+
{
270+
RNSentry *rnSentry = [[RNSentry alloc] init];
271+
NSError *error = nil;
272+
273+
NSDictionary *_Nonnull mockedReactNativeDictionary = @{
274+
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
275+
@"_experiments" : @ {
276+
@"enableUnhandledCPPExceptionsV2" : @NO,
277+
},
278+
};
279+
SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary
280+
error:&error];
281+
282+
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
283+
XCTAssertNil(error, @"Should not pass no error");
284+
285+
id experimentalOptions = [actualOptions valueForKey:@"experimental"];
286+
XCTAssertNotNil(experimentalOptions, @"Experimental options should not be nil");
287+
288+
BOOL enableUnhandledCPPExceptions =
289+
[[experimentalOptions valueForKey:@"enableUnhandledCPPExceptionsV2"] boolValue];
290+
XCTAssertFalse(
291+
enableUnhandledCPPExceptions, @"enableUnhandledCPPExceptionsV2 should be disabled");
292+
}
293+
294+
- (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Default
295+
{
296+
RNSentry *rnSentry = [[RNSentry alloc] init];
297+
NSError *error = nil;
298+
299+
NSDictionary *_Nonnull mockedReactNativeDictionary = @{
300+
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
301+
};
302+
SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary
303+
error:&error];
304+
305+
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
306+
XCTAssertNil(error, @"Should not pass no error");
307+
308+
// Test that when no _experiments are provided, the experimental option defaults to false
309+
id experimentalOptions = [actualOptions valueForKey:@"experimental"];
310+
XCTAssertNotNil(experimentalOptions, @"Experimental options should not be nil");
311+
312+
BOOL enableUnhandledCPPExceptions =
313+
[[experimentalOptions valueForKey:@"enableUnhandledCPPExceptionsV2"] boolValue];
314+
XCTAssertFalse(
315+
enableUnhandledCPPExceptions, @"enableUnhandledCPPExceptionsV2 should default to disabled");
316+
}
317+
242318
- (void)testPassesErrorOnWrongDsn
243319
{
244320
RNSentry *rnSentry = [[RNSentry alloc] init];

packages/core/ios/RNSentry.mm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,17 @@ - (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)
225225
// Failed requests can only be enabled in one SDK to avoid duplicates
226226
sentryOptions.enableCaptureFailedRequests = NO;
227227

228+
NSDictionary *experiments = options[@"_experiments"];
229+
if (experiments != nil && [experiments isKindOfClass:[NSDictionary class]]) {
230+
BOOL enableUnhandledCPPExceptions =
231+
[experiments[@"enableUnhandledCPPExceptionsV2"] boolValue];
232+
id experimentalOptions = [sentryOptions valueForKey:@"experimental"];
233+
if (experimentalOptions) {
234+
[experimentalOptions setValue:@(enableUnhandledCPPExceptions)
235+
forKey:@"enableUnhandledCPPExceptionsV2"];
236+
}
237+
}
238+
228239
return sentryOptions;
229240
}
230241

packages/core/src/js/options.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,17 @@ export interface BaseReactNativeOptions {
253253
* This will be removed in the next major version.
254254
*/
255255
replaysOnErrorSampleRate?: number;
256+
257+
/**
258+
* Experiment: A more reliable way to report unhandled C++ exceptions in iOS.
259+
*
260+
* This approach hooks into all instances of the `__cxa_throw` function, which provides a more comprehensive and consistent exception handling across an app’s runtime, regardless of the number of C++ modules or how they’re linked. It helps in obtaining accurate stack traces.
261+
*
262+
* - Note: The mechanism of hooking into `__cxa_throw` could cause issues with symbolication on iOS due to caching of symbol references.
263+
*
264+
* @default false
265+
*/
266+
enableUnhandledCPPExceptionsV2?: boolean;
256267
};
257268

258269
/**

samples/react-native/src/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ Sentry.init({
171171
// This should be disabled when manually initializing the native SDK
172172
// Note that options from JS are not passed to the native SDKs when initialized manually
173173
autoInitializeNativeSdk: true,
174+
_experiments: {
175+
enableUnhandledCPPExceptionsV2: true,
176+
},
174177
});
175178

176179
const Stack = isMobileOs

0 commit comments

Comments
 (0)