From ba58d4503288eae2499813c477b721ecfbca47fa Mon Sep 17 00:00:00 2001 From: Noah Martin Date: Thu, 6 Nov 2025 10:44:15 -0500 Subject: [PATCH 1/3] ref: Move options to wrapper --- .../project.pbxproj | 6 + .../RNSentryCocoaTesterTests/RNSentryTests.m | 259 +++++++++++------- packages/core/ios/RNSentry+fetchNativeStack.m | 8 - packages/core/ios/RNSentry.h | 3 +- packages/core/ios/RNSentry.mm | 212 ++++---------- packages/core/ios/SentrySDKWrapper.h | 16 ++ packages/core/ios/SentrySDKWrapper.m | 135 +++++++++ 7 files changed, 371 insertions(+), 268 deletions(-) diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj b/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj index 1c99c3a801..240f36b530 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ 33F58ACF2977037D008F60EA /* RNSentryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSentryTests.m; sourceTree = ""; }; 650CB718ACFBD05609BF2126 /* libPods-RNSentryCocoaTesterTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNSentryCocoaTesterTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; E2321E7CFA55AB617247098E /* Pods-RNSentryCocoaTesterTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryCocoaTesterTests.debug.xcconfig"; path = "Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests.debug.xcconfig"; sourceTree = ""; }; + FADF868E2EBD053E00D6652D /* SentrySDKWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentrySDKWrapper.h; path = ../ios/SentrySDKWrapper.h; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -136,6 +137,7 @@ 3360843A2C32E3A8008CC412 /* RNSentryReplayBreadcrumbConverter.h */, 330F308D2C0F385A002A0D4E /* RNSentryBreadcrumb.h */, 33958C672BFCEF5A00AD1FB6 /* RNSentryOnDrawReporter.h */, + FADF868E2EBD053E00D6652D /* SentrySDKWrapper.h */, 33AFE0132B8F31AF00AAB120 /* RNSentryDependencyContainer.h */, 338739072A7D7D2800950DDD /* RNSentryReplay.h */, ); @@ -236,10 +238,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources.sh\"\n"; diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m index 9e600cdbda..d4cd8e957d 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m @@ -1,4 +1,6 @@ #import "RNSentryTests.h" +#import "RNSentryReplay.h" +#import "SentrySDKWrapper.h" #import #import #import @@ -25,8 +27,11 @@ - (void)testCreateOptionsWithDictionaryRemovesPerformanceProperties , @"enableTracing" : @YES, } ; -SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; +mockedReactNativeDictionary = [rnSentry prepareOptions:mockedReactNativeDictionary]; +SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -40,14 +45,15 @@ - (void)testCreateOptionsWithDictionaryRemovesPerformanceProperties - (void)testCaptureFailedRequestsIsDisabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -56,14 +62,15 @@ - (void)testCaptureFailedRequestsIsDisabled - (void)testCreateOptionsWithDictionaryNativeCrashHandlingDefault { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertTrue(actualOptions.enableCrashHandler, @"Did not set native crash handling"); @@ -71,14 +78,15 @@ - (void)testCreateOptionsWithDictionaryNativeCrashHandlingDefault - (void)testCreateOptionsWithDictionaryAutoPerformanceTracingDefault { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertEqual( @@ -87,15 +95,16 @@ - (void)testCreateOptionsWithDictionaryAutoPerformanceTracingDefault - (void)testCreateOptionsWithDictionaryNativeCrashHandlingEnabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"enableNativeCrashHandling" : @YES, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertTrue(actualOptions.enableCrashHandler, @"Did not set native crash handling"); @@ -103,15 +112,16 @@ - (void)testCreateOptionsWithDictionaryNativeCrashHandlingEnabled - (void)testCreateOptionsWithDictionaryAutoPerformanceTracingEnabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"enableAutoPerformanceTracing" : @YES, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertEqual( @@ -120,15 +130,16 @@ - (void)testCreateOptionsWithDictionaryAutoPerformanceTracingEnabled - (void)testCreateOptionsWithDictionaryNativeCrashHandlingDisabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"enableNativeCrashHandling" : @NO, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertFalse(actualOptions.enableCrashHandler, @"Did not disable native crash handling"); @@ -136,15 +147,16 @@ - (void)testCreateOptionsWithDictionaryNativeCrashHandlingDisabled - (void)testCreateOptionsWithDictionaryAutoPerformanceTracingDisabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"enableAutoPerformanceTracing" : @NO, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertEqual(actualOptions.enableAutoPerformanceTracing, false, @@ -153,7 +165,6 @@ - (void)testCreateOptionsWithDictionaryAutoPerformanceTracingDisabled - (void)testCreateOptionsWithDictionarySpotlightEnabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @@ -161,8 +172,10 @@ - (void)testCreateOptionsWithDictionarySpotlightEnabled @"spotlight" : @YES, @"defaultSidecarUrl" : @"http://localhost:8969/teststream", }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertTrue(actualOptions.enableSpotlight, @"Did not enable spotlight"); @@ -171,7 +184,6 @@ - (void)testCreateOptionsWithDictionarySpotlightEnabled - (void)testCreateOptionsWithDictionarySpotlightOne { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @@ -179,8 +191,10 @@ - (void)testCreateOptionsWithDictionarySpotlightOne @"spotlight" : @1, @"defaultSidecarUrl" : @"http://localhost:8969/teststream", }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertTrue(actualOptions.enableSpotlight, @"Did not enable spotlight"); @@ -189,15 +203,16 @@ - (void)testCreateOptionsWithDictionarySpotlightOne - (void)testCreateOptionsWithDictionarySpotlightUrl { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"spotlight" : @"http://localhost:8969/teststream", }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertTrue(actualOptions.enableSpotlight, @"Did not enable spotlight"); @@ -206,15 +221,16 @@ - (void)testCreateOptionsWithDictionarySpotlightUrl - (void)testCreateOptionsWithDictionarySpotlightDisabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"spotlight" : @NO, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertFalse(actualOptions.enableSpotlight, @"Did not disable spotlight"); @@ -222,15 +238,16 @@ - (void)testCreateOptionsWithDictionarySpotlightDisabled - (void)testCreateOptionsWithDictionarySpotlightZero { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"spotlight" : @0, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); XCTAssertFalse(actualOptions.enableSpotlight, @"Did not disable spotlight"); @@ -238,7 +255,6 @@ - (void)testCreateOptionsWithDictionarySpotlightZero - (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Enabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @@ -247,8 +263,10 @@ - (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Enabled @"enableUnhandledCPPExceptionsV2" : @YES, }, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -264,7 +282,6 @@ - (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Enabled - (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Disabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @@ -273,8 +290,10 @@ - (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Disabled @"enableUnhandledCPPExceptionsV2" : @NO, }, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -290,14 +309,15 @@ - (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Disabled - (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Default { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -314,15 +334,16 @@ - (void)testCreateOptionsWithDictionaryEnableUnhandledCPPExceptionsV2Default - (void)testCreateOptionsWithDictionaryEnableLogsEnabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"enableLogs" : @YES, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -336,15 +357,16 @@ - (void)testCreateOptionsWithDictionaryEnableLogsEnabled - (void)testCreateOptionsWithDictionaryEnableLogsDisabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"enableLogs" : @NO, }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -358,14 +380,15 @@ - (void)testCreateOptionsWithDictionaryEnableLogsDisabled - (void)testPassesErrorOnWrongDsn { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"not_a_valid_dsn", }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNil(actualOptions, @"Created invalid sentry options"); XCTAssertNotNil(error, @"Did not created error on invalid dsn"); @@ -380,8 +403,10 @@ - (void)testBeforeBreadcrumbsCallbackFiltersOutSentryDsnRequestBreadcrumbs @"dsn" : @"https://abc@def.ingest.sentry.io/1234567", @"devServerUrl" : @"http://localhost:8081" }; - SentryOptions *options = [rnSentry createOptionsWithDictionary:mockedDictionary error:&error]; - + mockedDictionary = [rnSentry prepareOptions:mockedDictionary]; + SentryOptions *options = [SentrySDKWrapper createOptionsWithDictionary:mockedDictionary + isSessionReplayEnabled:NO + error:&error]; SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; breadcrumb.type = @"http"; breadcrumb.data = @{ @"url" : @"https://def.ingest.sentry.io/1234567" }; @@ -393,15 +418,15 @@ - (void)testBeforeBreadcrumbsCallbackFiltersOutSentryDsnRequestBreadcrumbs - (void)testBeforeBreadcrumbsCallbackFiltersOutDevServerRequestBreadcrumbs { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSString *mockDevServer = @"http://localhost:8081"; NSDictionary *_Nonnull mockedDictionary = @{ @"dsn" : @"https://abc@def.ingest.sentry.io/1234567", @"devServerUrl" : mockDevServer }; - SentryOptions *options = [rnSentry createOptionsWithDictionary:mockedDictionary error:&error]; - + SentryOptions *options = [SentrySDKWrapper createOptionsWithDictionary:mockedDictionary + isSessionReplayEnabled:NO + error:&error]; SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; breadcrumb.type = @"http"; breadcrumb.data = @{ @"url" : mockDevServer }; @@ -413,15 +438,15 @@ - (void)testBeforeBreadcrumbsCallbackFiltersOutDevServerRequestBreadcrumbs - (void)testBeforeBreadcrumbsCallbackDoesNotFiltersOutNonDevServerOrDsnRequestBreadcrumbs { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedDictionary = @{ @"dsn" : @"https://abc@def.ingest.sentry.io/1234567", @"devServerUrl" : @"http://localhost:8081" }; - SentryOptions *options = [rnSentry createOptionsWithDictionary:mockedDictionary error:&error]; - + SentryOptions *options = [SentrySDKWrapper createOptionsWithDictionary:mockedDictionary + isSessionReplayEnabled:NO + error:&error]; SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; breadcrumb.type = @"http"; breadcrumb.data = @{ @"url" : @"http://testurl.com/service" }; @@ -433,14 +458,14 @@ - (void)testBeforeBreadcrumbsCallbackDoesNotFiltersOutNonDevServerOrDsnRequestBr - (void)testBeforeBreadcrumbsCallbackKeepsBreadcrumbWhenDevServerUrlIsNotPassedAndDsnDoesNotMatch { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedDictionary = @{ // dsn is always validated in SentryOptions initialization @"dsn" : @"https://abc@def.ingest.sentry.io/1234567" }; - SentryOptions *options = [rnSentry createOptionsWithDictionary:mockedDictionary error:&error]; - + SentryOptions *options = [SentrySDKWrapper createOptionsWithDictionary:mockedDictionary + isSessionReplayEnabled:NO + error:&error]; SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; breadcrumb.type = @"http"; breadcrumb.data = @{ @"url" : @"http://testurl.com/service" }; @@ -621,11 +646,14 @@ - (void)testIgnoreErrorsDropsMatchingExceptionValue { RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; - NSDictionary *mockedOptions = @{ + NSMutableDictionary *mockedOptions = [@{ @"dsn" : @"https://abc@def.ingest.sentry.io/1234567", @"ignoreErrorsRegex" : @[ @"IgnoreMe.*" ] - }; - SentryOptions *options = [rnSentry createOptionsWithDictionary:mockedOptions error:&error]; + } mutableCopy]; + mockedOptions = [rnSentry prepareOptions:mockedOptions]; + SentryOptions *options = [SentrySDKWrapper createOptionsWithDictionary:mockedOptions + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(options); XCTAssertNil(error); SentryEvent *event = [[SentryEvent alloc] init]; @@ -640,11 +668,15 @@ - (void)testIgnoreErrorsDropsMatchingEventMessage { RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; - NSDictionary *mockedOptions = @{ + NSMutableDictionary *mockedOptions = [@{ @"dsn" : @"https://abc@def.ingest.sentry.io/1234567", @"ignoreErrorsStr" : @[ @"DropThisError" ] - }; - SentryOptions *options = [rnSentry createOptionsWithDictionary:mockedOptions error:&error]; + } mutableCopy]; + mockedOptions = [rnSentry prepareOptions:mockedOptions]; + SentryOptions *options = [SentrySDKWrapper createOptionsWithDictionary:mockedOptions + isSessionReplayEnabled:NO + error:&error]; + XCTAssertNotNil(options); XCTAssertNotNil(options); XCTAssertNil(error); SentryEvent *event = [[SentryEvent alloc] init]; @@ -659,11 +691,15 @@ - (void)testIgnoreErrorsDoesNotDropNonMatchingEvent { RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; - NSDictionary *mockedOptions = @{ + NSMutableDictionary *mockedOptions = [@{ @"dsn" : @"https://abc@def.ingest.sentry.io/1234567", @"ignoreErrorsRegex" : @[ @"IgnoreMe.*" ] - }; - SentryOptions *options = [rnSentry createOptionsWithDictionary:mockedOptions error:&error]; + } mutableCopy]; + mockedOptions = [rnSentry prepareOptions:mockedOptions]; + SentryOptions *options = [SentrySDKWrapper createOptionsWithDictionary:mockedOptions + isSessionReplayEnabled:NO + error:&error]; + XCTAssertNotNil(options); XCTAssertNotNil(options); XCTAssertNil(error); SentryEvent *event = [[SentryEvent alloc] init]; @@ -681,11 +717,15 @@ - (void)testIgnoreErrorsDropsMatchingExactString { RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; - NSDictionary *mockedOptions = @{ + NSMutableDictionary *mockedOptions = [@{ @"dsn" : @"https://abc@def.ingest.sentry.io/1234567", @"ignoreErrorsStr" : @[ @"ExactError" ] - }; - SentryOptions *options = [rnSentry createOptionsWithDictionary:mockedOptions error:&error]; + } mutableCopy]; + mockedOptions = [rnSentry prepareOptions:mockedOptions]; + SentryOptions *options = [SentrySDKWrapper createOptionsWithDictionary:mockedOptions + isSessionReplayEnabled:NO + error:&error]; + XCTAssertNotNil(options); XCTAssertNotNil(options); XCTAssertNil(error); SentryEvent *event = [[SentryEvent alloc] init]; @@ -700,13 +740,17 @@ - (void)testIgnoreErrorsRegexAndStringBothWork { RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; - NSDictionary *mockedOptions = @{ + NSMutableDictionary *mockedOptions = [@{ @"dsn" : @"https://abc@def.ingest.sentry.io/1234567", @"ignoreErrorsStr" : @[ @"ExactError" ], @"ignoreErrorsRegex" : @[ @"IgnoreMe.*" ], - }; - SentryOptions *options = [rnSentry createOptionsWithDictionary:mockedOptions error:&error]; + } mutableCopy]; + mockedOptions = [rnSentry prepareOptions:mockedOptions]; + SentryOptions *options = [SentrySDKWrapper createOptionsWithDictionary:mockedOptions + isSessionReplayEnabled:NO + error:&error]; + XCTAssertNotNil(options); XCTAssertNotNil(options); XCTAssertNil(error); // Test regex match @@ -734,14 +778,15 @@ - (void)testIgnoreErrorsRegexAndStringBothWork - (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentDefault { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -757,16 +802,18 @@ - (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmen - (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentWithErrorSampleRate { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; - NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + NSMutableDictionary *_Nonnull mockedReactNativeDictionary = [@{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"replaysOnErrorSampleRate" : @1.0, @"replaysSessionSampleRate" : @0 - }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + } mutableCopy]; + BOOL enableReplay = [RNSentryReplay updateOptions:mockedReactNativeDictionary]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:enableReplay + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -783,17 +830,18 @@ - (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmen - (void) testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentWithSessionSampleRate { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; - NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + NSMutableDictionary *_Nonnull mockedReactNativeDictionary = [@{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"replaysOnErrorSampleRate" : @0.0, @"replaysSessionSampleRate" : @0.1 - }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; - + } mutableCopy]; + BOOL enableReplay = [RNSentryReplay updateOptions:mockedReactNativeDictionary]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:enableReplay + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -809,16 +857,18 @@ - (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmen - (void) testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentWithSessionSampleRates { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; - NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + NSMutableDictionary *_Nonnull mockedReactNativeDictionary = [@{ @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", @"replaysOnErrorSampleRate" : @1.0, @"replaysSessionSampleRate" : @0.1 - }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + } mutableCopy]; + BOOL enableReplay = [RNSentryReplay updateOptions:mockedReactNativeDictionary]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:enableReplay + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); @@ -834,7 +884,6 @@ - (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmen - (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentDisabled { - RNSentry *rnSentry = [[RNSentry alloc] init]; NSError *error = nil; NSDictionary *_Nonnull mockedReactNativeDictionary = @{ @@ -842,8 +891,10 @@ - (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmen @"replaysOnErrorSampleRate" : @0, @"replaysSessionSampleRate" : @0 }; - SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary - error:&error]; + SentryOptions *actualOptions = + [SentrySDKWrapper createOptionsWithDictionary:mockedReactNativeDictionary + isSessionReplayEnabled:NO + error:&error]; XCTAssertNotNil(actualOptions, @"Did not create sentry options"); XCTAssertNil(error, @"Should not pass no error"); diff --git a/packages/core/ios/RNSentry+fetchNativeStack.m b/packages/core/ios/RNSentry+fetchNativeStack.m index 04f5e7e140..d10f82108c 100644 --- a/packages/core/ios/RNSentry+fetchNativeStack.m +++ b/packages/core/ios/RNSentry+fetchNativeStack.m @@ -2,14 +2,6 @@ #import "RNSentryBreadcrumb.h" #import "RNSentryId.h" #import -#import -#import -#import -#import -#import -#import -#import -#import @import Sentry; // This method was moved to a new category so we can use `@import Sentry` to use Sentry's Swift diff --git a/packages/core/ios/RNSentry.h b/packages/core/ios/RNSentry.h index 373d1aec46..5b163baca9 100644 --- a/packages/core/ios/RNSentry.h +++ b/packages/core/ios/RNSentry.h @@ -20,8 +20,7 @@ typedef int (*SymbolicateCallbackType)(const void *, Dl_info *); @interface RNSentry : RCTEventEmitter -- (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)options - error:(NSError *_Nullable *_Nonnull)errorPointer; +- (NSMutableDictionary *)prepareOptions:(NSDictionary *)options; - (void)setEventOriginTag:(SentryEvent *)event; diff --git a/packages/core/ios/RNSentry.mm b/packages/core/ios/RNSentry.mm index 47c52332ee..10586ab910 100644 --- a/packages/core/ios/RNSentry.mm +++ b/packages/core/ios/RNSentry.mm @@ -26,8 +26,6 @@ #import #import #import -#import -#import #import #import @@ -85,24 +83,68 @@ - (instancetype)init return self; } +- (NSMutableDictionary *)prepareOptions:(NSDictionary *)options +{ + SentryBeforeSendEventCallback beforeSend = ^SentryEvent *(SentryEvent *event) { + // We don't want to send an event after startup that came from a Unhandled JS Exception of + // React Native because we sent it already before the app crashed. + if (nil != event.exceptions.firstObject.type && + [event.exceptions.firstObject.type rangeOfString:@"Unhandled JS Exception"].location + != NSNotFound) { + return nil; + } + + // Regex and Str are set when one of them has value so we only need to check one of them. + if (self->_ignoreErrorPatternsStr || self->_ignoreErrorPatternsRegex) { + for (SentryException *exception in event.exceptions) { + if ([self shouldIgnoreError:exception.value]) { + return nil; + } + } + if ([self shouldIgnoreError:event.message.message]) { + return nil; + } + } + + [self setEventOriginTag:event]; + return event; + }; + + NSMutableDictionary *mutableOptions = [options mutableCopy]; + [mutableOptions setValue:beforeSend forKey:@"beforeSend"]; + + // remove performance traces sample rate and traces sampler since we don't want to synchronize + // these configurations to the Native SDKs. The user could tho initialize the SDK manually and + // set themselves. + [mutableOptions removeObjectForKey:@"tracesSampleRate"]; + [mutableOptions removeObjectForKey:@"tracesSampler"]; + [mutableOptions removeObjectForKey:@"enableTracing"]; + + [self trySetIgnoreErrors:mutableOptions]; + + return mutableOptions; +} + RCT_EXPORT_MODULE() RCT_EXPORT_METHOD(initNativeSdk : (NSDictionary *_Nonnull)options resolve : ( RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) { + NSMutableDictionary *mutableOptions = [self prepareOptions:options]; +#if SENTRY_TARGET_REPLAY_SUPPORTED + BOOL isSessionReplayEnabled = [RNSentryReplay updateOptions:mutableOptions]; +#else + // Defaulting to false for unsupported targets + BOOL isSessionReplayEnabled = NO; +#endif NSError *error = nil; - SentryOptions *sentryOptions = [self createOptionsWithDictionary:options error:&error]; + [SentrySDKWrapper setupWithDictionary:mutableOptions + isSessionReplayEnabled:isSessionReplayEnabled + error:&error]; if (error != nil) { reject(@"SentryReactNative", error.localizedDescription, error); return; } - NSString *sdkVersion = [PrivateSentrySDKOnly getSdkVersionString]; - [PrivateSentrySDKOnly setSdkName:NATIVE_SDK_NAME andVersionString:sdkVersion]; - [PrivateSentrySDKOnly addSdkPackage:REACT_NATIVE_SDK_PACKAGE_NAME - version:REACT_NATIVE_SDK_PACKAGE_VERSION]; - - [SentrySDKWrapper startWithOptions:sentryOptions]; - #if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST BOOL appIsActive = [[UIApplication sharedApplication] applicationState] == UIApplicationStateActive; @@ -113,8 +155,8 @@ - (instancetype)init // If the app is active/in foreground, and we have not sent the SentryHybridSdkDidBecomeActive // notification, send it. if (appIsActive && !sentHybridSdkDidBecomeActive - && (PrivateSentrySDKOnly.options.enableAutoSessionTracking - || PrivateSentrySDKOnly.options.enableWatchdogTerminationTracking)) { + && ([SentrySDKWrapper enableAutoSessionTracking] || + [SentrySDKWrapper enableWatchdogTerminationTracking])) { [[NSNotificationCenter defaultCenter] postNotificationName:@"SentryHybridSdkDidBecomeActive" object:nil]; @@ -128,7 +170,7 @@ - (instancetype)init resolve(@YES); } -- (void)trySetIgnoreErrors:(NSMutableDictionary *)options +- (void)trySetIgnoreErrors:(NSDictionary *)options { NSArray *ignoreErrorsStr = nil; NSArray *ignoreErrorsRegex = nil; @@ -194,144 +236,6 @@ - (BOOL)shouldIgnoreError:(NSString *)message return NO; } -- (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)options - error:(NSError *_Nonnull *_Nonnull)errorPointer -{ - SentryBeforeSendEventCallback beforeSend = ^SentryEvent *(SentryEvent *event) { - // We don't want to send an event after startup that came from a Unhandled JS Exception of - // React Native because we sent it already before the app crashed. - if (nil != event.exceptions.firstObject.type && - [event.exceptions.firstObject.type rangeOfString:@"Unhandled JS Exception"].location - != NSNotFound) { - return nil; - } - - // Regex and Str are set when one of them has value so we only need to check one of them. - if (self->_ignoreErrorPatternsStr || self->_ignoreErrorPatternsRegex) { - for (SentryException *exception in event.exceptions) { - if ([self shouldIgnoreError:exception.value]) { - return nil; - } - } - if ([self shouldIgnoreError:event.message.message]) { - return nil; - } - } - - [self setEventOriginTag:event]; - return event; - }; - - NSMutableDictionary *mutableOptions = [options mutableCopy]; - [mutableOptions setValue:beforeSend forKey:@"beforeSend"]; - - // remove performance traces sample rate and traces sampler since we don't want to synchronize - // these configurations to the Native SDKs. The user could tho initialize the SDK manually and - // set themselves. - [mutableOptions removeObjectForKey:@"tracesSampleRate"]; - [mutableOptions removeObjectForKey:@"tracesSampler"]; - [mutableOptions removeObjectForKey:@"enableTracing"]; - -#if SENTRY_TARGET_REPLAY_SUPPORTED - BOOL isSessionReplayEnabled = [RNSentryReplay updateOptions:mutableOptions]; -#else - // Defaulting to false for unsupported targets - BOOL isSessionReplayEnabled = NO; -#endif - - SentryOptions *sentryOptions = [SentryOptionsInternal initWithDict:mutableOptions - didFailWithError:errorPointer]; - if (*errorPointer != nil) { - return nil; - } - - // Exclude Dev Server and Sentry Dsn request from Breadcrumbs - NSString *dsn = [self getURLFromDSN:[mutableOptions valueForKey:@"dsn"]]; - NSString *devServerUrl = [mutableOptions valueForKey:@"devServerUrl"]; - sentryOptions.beforeBreadcrumb - = ^SentryBreadcrumb *_Nullable(SentryBreadcrumb *_Nonnull breadcrumb) - { - NSString *url = breadcrumb.data[@"url"] ?: @""; - - if ([@"http" isEqualToString:breadcrumb.type] - && ((dsn != nil && [url hasPrefix:dsn]) - || (devServerUrl != nil && [url hasPrefix:devServerUrl]))) { - return nil; - } - return breadcrumb; - }; - - if ([mutableOptions valueForKey:@"enableNativeCrashHandling"] != nil) { - BOOL enableNativeCrashHandling = [mutableOptions[@"enableNativeCrashHandling"] boolValue]; - - if (!enableNativeCrashHandling) { - sentryOptions.enableCrashHandler = NO; - } - } - - // Set spotlight option - if ([mutableOptions valueForKey:@"spotlight"] != nil) { - id spotlightValue = [mutableOptions valueForKey:@"spotlight"]; - if ([spotlightValue isKindOfClass:[NSString class]]) { - NSLog(@"Using Spotlight on address: %@", spotlightValue); - sentryOptions.enableSpotlight = true; - sentryOptions.spotlightUrl = spotlightValue; - } else if ([spotlightValue isKindOfClass:[NSNumber class]]) { - sentryOptions.enableSpotlight = [spotlightValue boolValue]; - id defaultSpotlightUrl = [mutableOptions valueForKey:@"defaultSidecarUrl"]; - if (defaultSpotlightUrl != nil) { - sentryOptions.spotlightUrl = defaultSpotlightUrl; - } - } - } - - if ([mutableOptions valueForKey:@"enableLogs"] != nil) { - id enableLogsValue = [mutableOptions valueForKey:@"enableLogs"]; - if ([enableLogsValue isKindOfClass:[NSNumber class]]) { - [RNSentryExperimentalOptions setEnableLogs:[enableLogsValue boolValue] - sentryOptions:sentryOptions]; - } - } - [self trySetIgnoreErrors:mutableOptions]; - - // Enable the App start and Frames tracking measurements - if ([mutableOptions valueForKey:@"enableAutoPerformanceTracing"] != nil) { - BOOL enableAutoPerformanceTracing = - [mutableOptions[@"enableAutoPerformanceTracing"] boolValue]; - PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = enableAutoPerformanceTracing; -#if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST - PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode = enableAutoPerformanceTracing; -#endif - } - - // Failed requests can only be enabled in one SDK to avoid duplicates - sentryOptions.enableCaptureFailedRequests = NO; - - NSDictionary *experiments = options[@"_experiments"]; - if (experiments != nil && [experiments isKindOfClass:[NSDictionary class]]) { - BOOL enableUnhandledCPPExceptions = - [experiments[@"enableUnhandledCPPExceptionsV2"] boolValue]; - [RNSentryExperimentalOptions setEnableUnhandledCPPExceptionsV2:enableUnhandledCPPExceptions - sentryOptions:sentryOptions]; - } - - if (isSessionReplayEnabled) { - [RNSentryExperimentalOptions setEnableSessionReplayInUnreliableEnvironment:YES - sentryOptions:sentryOptions]; - } - - return sentryOptions; -} - -- (NSString *_Nullable)getURLFromDSN:(NSString *)dsn -{ - NSURL *url = [NSURL URLWithString:dsn]; - if (!url) { - return nil; - } - return [NSString stringWithFormat:@"%@://%@", url.scheme, url.host]; -} - - (void)setEventOriginTag:(SentryEvent *)event { if (event.sdk != nil) { @@ -460,7 +364,7 @@ - (void)stopObserving contexts[@"os"] = os; } - NSString *releaseName = SentrySDKInternal.options.releaseName; + NSString *releaseName = [SentrySDKWrapper releaseName]; if (releaseName) { contexts[@"release"] = releaseName; } @@ -492,7 +396,7 @@ - (void)stopObserving RCT_EXPORT_METHOD(fetchNativeDeviceContexts : (RCTPromiseResolveBlock)resolve rejecter : ( RCTPromiseRejectBlock)reject) { - if (PrivateSentrySDKOnly.options.debug) { + if ([SentrySDKWrapper debug]) { NSLog(@"Bridge call to: deviceContexts"); } __block NSMutableDictionary *serializedScope; @@ -507,7 +411,7 @@ - (void)stopObserving forKey:@"user"]; } - if (PrivateSentrySDKOnly.options.debug) { + if ([SentrySDKWrapper debug]) { NSData *data = [NSJSONSerialization dataWithJSONObject:serializedScope options:0 error:nil]; diff --git a/packages/core/ios/SentrySDKWrapper.h b/packages/core/ios/SentrySDKWrapper.h index 9040d032ab..ce6463d77f 100644 --- a/packages/core/ios/SentrySDKWrapper.h +++ b/packages/core/ios/SentrySDKWrapper.h @@ -15,4 +15,20 @@ + (void)startWithOptions:(SentryOptions *)options; ++ (SentryOptions *)createOptionsWithDictionary:(NSDictionary *)options + isSessionReplayEnabled:(BOOL)isSessionReplayEnabled + error:(NSError **)errorPointer; + ++ (void)setupWithDictionary:(NSDictionary *)options + isSessionReplayEnabled:(BOOL)isSessionReplayEnabled + error:(NSError **)errorPointer; + ++ (BOOL)debug; + ++ (NSString *)releaseName; + ++ (BOOL)enableAutoSessionTracking; + ++ (BOOL)enableWatchdogTerminationTracking; + @end diff --git a/packages/core/ios/SentrySDKWrapper.m b/packages/core/ios/SentrySDKWrapper.m index 0845f1ff3e..c11782d5ad 100644 --- a/packages/core/ios/SentrySDKWrapper.m +++ b/packages/core/ios/SentrySDKWrapper.m @@ -1,4 +1,6 @@ #import "SentrySDKWrapper.h" +#import "RNSentryExperimentalOptions.h" +#import "RNSentryVersion.h" @import Sentry; @implementation SentrySDKWrapper @@ -28,4 +30,137 @@ + (void)configureScope:(void (^)(SentryScope *scope))callback [SentrySDK configureScope:callback]; } ++ (SentryOptions *)createOptionsWithDictionary:(NSDictionary *)options + isSessionReplayEnabled:(BOOL)isSessionReplayEnabled + error:(NSError *__autoreleasing *)errorPointer +{ + NSString *dsn = [self getURLFromDSN:[options valueForKey:@"dsn"]]; + SentryOptions *sentryOptions = [SentryOptionsInternal initWithDict:options + didFailWithError:errorPointer]; + if (*errorPointer != nil) { + return nil; + } + + // Exclude Dev Server and Sentry Dsn request from Breadcrumbs + NSString *devServerUrl = [options valueForKey:@"devServerUrl"]; + sentryOptions.beforeBreadcrumb + = ^SentryBreadcrumb *_Nullable(SentryBreadcrumb *_Nonnull breadcrumb) + { + NSString *url = breadcrumb.data[@"url"] ?: @""; + + if ([@"http" isEqualToString:breadcrumb.type] + && ((dsn != nil && [url hasPrefix:dsn]) + || (devServerUrl != nil && [url hasPrefix:devServerUrl]))) { + return nil; + } + return breadcrumb; + }; + + if ([options valueForKey:@"enableNativeCrashHandling"] != nil) { + BOOL enableNativeCrashHandling = [options[@"enableNativeCrashHandling"] boolValue]; + + if (!enableNativeCrashHandling) { + sentryOptions.enableCrashHandler = NO; + } + } + + // Set spotlight option + if ([options valueForKey:@"spotlight"] != nil) { + id spotlightValue = [options valueForKey:@"spotlight"]; + if ([spotlightValue isKindOfClass:[NSString class]]) { + NSLog(@"Using Spotlight on address: %@", spotlightValue); + sentryOptions.enableSpotlight = true; + sentryOptions.spotlightUrl = spotlightValue; + } else if ([spotlightValue isKindOfClass:[NSNumber class]]) { + sentryOptions.enableSpotlight = [spotlightValue boolValue]; + id defaultSpotlightUrl = [options valueForKey:@"defaultSidecarUrl"]; + if (defaultSpotlightUrl != nil) { + sentryOptions.spotlightUrl = defaultSpotlightUrl; + } + } + } + + if ([options valueForKey:@"enableLogs"] != nil) { + id enableLogsValue = [options valueForKey:@"enableLogs"]; + if ([enableLogsValue isKindOfClass:[NSNumber class]]) { + [RNSentryExperimentalOptions setEnableLogs:[enableLogsValue boolValue] + sentryOptions:sentryOptions]; + } + } + + // Enable the App start and Frames tracking measurements + if ([options valueForKey:@"enableAutoPerformanceTracing"] != nil) { + BOOL enableAutoPerformanceTracing = [options[@"enableAutoPerformanceTracing"] boolValue]; + PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = enableAutoPerformanceTracing; +#if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST + PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode = enableAutoPerformanceTracing; +#endif + } + + // Failed requests can only be enabled in one SDK to avoid duplicates + sentryOptions.enableCaptureFailedRequests = NO; + + NSDictionary *experiments = options[@"_experiments"]; + if (experiments != nil && [experiments isKindOfClass:[NSDictionary class]]) { + BOOL enableUnhandledCPPExceptions = + [experiments[@"enableUnhandledCPPExceptionsV2"] boolValue]; + [RNSentryExperimentalOptions setEnableUnhandledCPPExceptionsV2:enableUnhandledCPPExceptions + sentryOptions:sentryOptions]; + } + + if (isSessionReplayEnabled) { + [RNSentryExperimentalOptions setEnableSessionReplayInUnreliableEnvironment:YES + sentryOptions:sentryOptions]; + } + return sentryOptions; +} + ++ (NSString *_Nullable)getURLFromDSN:(NSString *)dsn +{ + NSURL *url = [NSURL URLWithString:dsn]; + if (!url) { + return nil; + } + return [NSString stringWithFormat:@"%@://%@", url.scheme, url.host]; +} + ++ (void)setupWithDictionary:(NSDictionary *_Nonnull)options + isSessionReplayEnabled:(BOOL)isSessionReplayEnabled + error:(NSError *_Nonnull *_Nonnull)errorPointer +{ + SentryOptions *sentryOptions = [self createOptionsWithDictionary:options + isSessionReplayEnabled:isSessionReplayEnabled + error:errorPointer]; + if (!options) { + return; + } + + NSString *sdkVersion = [PrivateSentrySDKOnly getSdkVersionString]; + [PrivateSentrySDKOnly setSdkName:NATIVE_SDK_NAME andVersionString:sdkVersion]; + [PrivateSentrySDKOnly addSdkPackage:REACT_NATIVE_SDK_PACKAGE_NAME + version:REACT_NATIVE_SDK_PACKAGE_VERSION]; + + [SentrySDKWrapper startWithOptions:sentryOptions]; +} + ++ (BOOL)debug +{ + return PrivateSentrySDKOnly.options.debug; +} + ++ (NSString *)releaseName +{ + return PrivateSentrySDKOnly.options.releaseName; +} + ++ (BOOL)enableAutoSessionTracking +{ + return PrivateSentrySDKOnly.options.enableAutoSessionTracking; +} + ++ (BOOL)enableWatchdogTerminationTracking +{ + return PrivateSentrySDKOnly.options.enableWatchdogTerminationTracking; +} + @end From 81ffec77c04cce2a88b86a5092738f6dbdcd0fc8 Mon Sep 17 00:00:00 2001 From: Noah Martin Date: Fri, 7 Nov 2025 11:45:34 -0800 Subject: [PATCH 2/3] Update packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj Co-authored-by: Antonis Lilis --- .../RNSentryCocoaTester.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj b/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj index 240f36b530..118531d920 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj @@ -244,8 +244,6 @@ outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources.sh\"\n"; From 7ce6beec913f2aaa56352e83fa53aded59817526 Mon Sep 17 00:00:00 2001 From: Noah Martin Date: Fri, 7 Nov 2025 11:45:44 -0800 Subject: [PATCH 3/3] Update packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj Co-authored-by: Antonis Lilis --- .../RNSentryCocoaTester.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj b/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj index 118531d920..7ee546e8af 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj @@ -238,8 +238,6 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests-resources-${CONFIGURATION}-output-files.xcfilelist",