diff --git a/.github/workflows/sample-application.yml b/.github/workflows/sample-application.yml index 0ca61647c5..764bf88a6d 100644 --- a/.github/workflows/sample-application.yml +++ b/.github/workflows/sample-application.yml @@ -43,6 +43,7 @@ jobs: build-type: ['dev', 'production'] include: - platform: ios + xcode-version: '16.2' runs-on: macos-14 - platform: macos runs-on: macos-15 @@ -84,6 +85,9 @@ jobs: - name: Gradle cache uses: gradle/gradle-build-action@v3 + - run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode-version }}.app/Contents/Developer + if: ${{ matrix.platform == 'ios' }} + - name: Setup Global Xcode Tools if: ${{ matrix.platform == 'ios' }} run: which xcbeautify || brew install xcbeautify diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryReplayOptionsTests.swift b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryReplayOptionsTests.swift index ff3dccba6f..5dcd2be3ec 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryReplayOptionsTests.swift +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryReplayOptionsTests.swift @@ -67,11 +67,7 @@ final class RNSentryReplayOptions: XCTestCase { ] as NSDictionary).mutableCopy() as! NSMutableDictionary RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.sessionSampleRate, 0.75) } @@ -82,11 +78,7 @@ final class RNSentryReplayOptions: XCTestCase { ] as NSDictionary).mutableCopy() as! NSMutableDictionary RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.onErrorSampleRate, 0.75) } @@ -116,11 +108,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.maskAllImages, true) assertContainsClass(classArray: actualOptions.sessionReplay.maskedViewClasses, stringClass: "RCTImageView") @@ -135,11 +123,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.maskAllImages, false) XCTAssertEqual(actualOptions.sessionReplay.maskedViewClasses.count, 0) @@ -154,11 +138,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.maskAllText, true) assertContainsClass(classArray: actualOptions.sessionReplay.maskedViewClasses, stringClass: "RCTTextView") @@ -182,11 +162,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.maskAllText, false) XCTAssertEqual(actualOptions.sessionReplay.maskedViewClasses.count, 0) @@ -200,11 +176,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertTrue(actualOptions.sessionReplay.enableExperimentalViewRenderer) } @@ -218,11 +190,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertTrue(actualOptions.sessionReplay.enableViewRendererV2) } @@ -236,11 +204,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertFalse(actualOptions.sessionReplay.enableViewRendererV2) } @@ -253,11 +217,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertFalse(actualOptions.sessionReplay.enableFastViewRendering) } @@ -271,11 +231,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertTrue(actualOptions.sessionReplay.enableFastViewRendering) } @@ -289,15 +245,11 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertFalse(actualOptions.sessionReplay.enableFastViewRendering) } - + func testReplayQualityDefault() { let optionsDict = ([ "dsn": "https://abc@def.ingest.sentry.io/1234567", @@ -306,11 +258,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.medium) } @@ -324,11 +272,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.low) } @@ -342,11 +286,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.medium) } @@ -360,11 +300,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.high) } @@ -378,11 +314,7 @@ final class RNSentryReplayOptions: XCTestCase { RNSentryReplay.updateOptions(optionsDict) - #if CROSS_PLATFORM_TEST let actualOptions = try! SentryOptionsInternal.initWithDict(optionsDict as! [String: Any]) - #else - let actualOptions = try! Options(dict: optionsDict as! [String: Any]) - #endif XCTAssertEqual(actualOptions.sessionReplay.quality, SentryReplayOptions.SentryReplayQuality.medium) } diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.h b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.h index fa80410d6c..54e8504da0 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.h +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.h @@ -3,12 +3,8 @@ @class SentryOptions; -#if CROSS_PLATFORM_TEST @interface SentrySDKInternal (PrivateTests) -#else -@interface -SentrySDK (PrivateTests) -#endif + + (nullable SentryOptions *)options; @end diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m index b7940cad2b..9aa577f107 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m @@ -465,13 +465,8 @@ - (void)prepareNativeFrameMocksWithLocalSymbolication:(BOOL)debug SentryOptions *sentryOptions = [[SentryOptions alloc] init]; sentryOptions.debug = debug; // no local symbolication -#if CROSS_PLATFORM_TEST id sentrySDKMock = OCMClassMock([SentrySDKInternal class]); OCMStub([(Class)sentrySDKMock options]).andReturn(sentryOptions); -#else - id sentrySDKMock = OCMClassMock([SentrySDK class]); - OCMStub([(Class)sentrySDKMock options]).andReturn(sentryOptions); -#endif id sentryDependencyContainerMock = OCMClassMock([SentryDependencyContainer class]); OCMStub(ClassMethod([sentryDependencyContainerMock sharedInstance])) diff --git a/packages/core/ios/RNSentry+fetchNativeStack.m b/packages/core/ios/RNSentry+fetchNativeStack.m index 3e1f37a3b8..72515ff235 100644 --- a/packages/core/ios/RNSentry+fetchNativeStack.m +++ b/packages/core/ios/RNSentry+fetchNativeStack.m @@ -22,11 +22,8 @@ - (NSDictionary *)fetchNativeStackFramesBy:(NSArray *)instructionsAddr symbolicate:(SymbolicateCallbackType)symbolicate { -#if CROSS_PLATFORM_TEST BOOL shouldSymbolicateLocally = [SentrySDKInternal.options debug]; -#else - BOOL shouldSymbolicateLocally = [SentrySDK.options debug]; -#endif + NSString *appPackageName = [[NSBundle mainBundle] executablePath]; NSMutableSet *_Nonnull imagesAddrToRetrieveDebugMetaImages = diff --git a/packages/core/ios/RNSentry.h b/packages/core/ios/RNSentry.h index c13773f6c2..074c8f270e 100644 --- a/packages/core/ios/RNSentry.h +++ b/packages/core/ios/RNSentry.h @@ -15,12 +15,7 @@ typedef int (*SymbolicateCallbackType)(const void *, Dl_info *); @class SentryOptions; @class SentryEvent; -#if CROSS_PLATFORM_TEST @interface SentrySDKInternal : NSObject -#else -@interface -SentrySDK (Private) -#endif @property (nonatomic, nullable, readonly, class) SentryOptions *options; @end diff --git a/packages/core/ios/RNSentry.mm b/packages/core/ios/RNSentry.mm index 326af7bb41..8d65efc5b3 100644 --- a/packages/core/ios/RNSentry.mm +++ b/packages/core/ios/RNSentry.mm @@ -28,16 +28,9 @@ #import #import #import -#import - -#if __has_include() -# define USE_SENTRY_OPTIONS 1 -# import -#else -# define USE_SENTRY_OPTIONS 0 -# import -#endif +#import #import +#import // This guard prevents importing Hermes in JSC apps #if SENTRY_PROFILING_ENABLED @@ -247,13 +240,8 @@ - (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull) [RNSentryReplay updateOptions:mutableOptions]; #endif -#if USE_SENTRY_OPTIONS - SentryOptions *sentryOptions = [[SentryOptions alloc] initWithDict:mutableOptions - didFailWithError:errorPointer]; -#else SentryOptions *sentryOptions = [SentryOptionsInternal initWithDict:mutableOptions didFailWithError:errorPointer]; -#endif if (*errorPointer != nil) { return nil; } @@ -467,11 +455,7 @@ - (void)stopObserving contexts[@"os"] = os; } -#if CROSS_PLATFORM_TEST NSString *releaseName = SentrySDKInternal.options.releaseName; -#else - NSString *releaseName = [SentrySDK options].releaseName; -#endif if (releaseName) { contexts[@"release"] = releaseName; } diff --git a/samples/react-native/ios/SentryNativeInitializer.h b/samples/react-native/ios/SentryNativeInitializer.h new file mode 100644 index 0000000000..9d0c2b1d1a --- /dev/null +++ b/samples/react-native/ios/SentryNativeInitializer.h @@ -0,0 +1,7 @@ +#import + +@interface SentryNativeInitializer : NSObject + ++ (void)initializeSentry; + +@end diff --git a/samples/react-native/ios/SentryNativeInitializer.m b/samples/react-native/ios/SentryNativeInitializer.m new file mode 100644 index 0000000000..20a4bcaa44 --- /dev/null +++ b/samples/react-native/ios/SentryNativeInitializer.m @@ -0,0 +1,38 @@ +#import "SentryNativeInitializer.h" +@import Sentry; + +@implementation SentryNativeInitializer + ++ (void)initializeSentry +{ + [SentrySDK startWithConfigureOptions:^(SentryOptions *options) { + // Only options set here will apply to the iOS SDK + // Options from JS are not passed to the iOS SDK when initialized manually + options.dsn = @"https://1df17bd4e543fdb31351dee1768bb679@o447951.ingest.sentry.io/5428561"; + options.debug = YES; // Enabled debug when first installing is always helpful + + options.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) { + NSLog(@"Unhandled JS Exception"); + return nil; + } + + return event; + }; + + // Enable the App start and Frames tracking measurements + // If this is disabled the app start and frames tracking + // won't be passed from native to JS transactions + PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = true; +#if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST + PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode = true; +#endif + }]; +} + +@end diff --git a/samples/react-native/ios/sentryreactnativesample.xcodeproj/project.pbxproj b/samples/react-native/ios/sentryreactnativesample.xcodeproj/project.pbxproj index 0d2f979cb0..1350418ccb 100644 --- a/samples/react-native/ios/sentryreactnativesample.xcodeproj/project.pbxproj +++ b/samples/react-native/ios/sentryreactnativesample.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 5ACADB1A9924EDD0850FACBA /* libPods-sentryreactnativesample-sentryreactnativesampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AFC2BCCFBDE2DC231B5C04E5 /* libPods-sentryreactnativesample-sentryreactnativesampleTests.a */; }; 723FB93385E08A7032AE82F4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 916F55A329D66130A4DFF319 /* PrivacyInfo.xcprivacy */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + AE3C91AE2E3BB9B400DC7C20 /* SentryNativeInitializer.m in Sources */ = {isa = PBXBuildFile; fileRef = AE3C91AD2E3BB9B000DC7C20 /* SentryNativeInitializer.m */; }; F998F5A3F1731876C4EDA235 /* libPods-sentryreactnativesample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CA08EE94AE203638B8C8B74B /* libPods-sentryreactnativesample.a */; }; /* End PBXBuildFile section */ @@ -49,6 +50,8 @@ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = sentryreactnativesample/LaunchScreen.storyboard; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-sentryreactnativesample-sentryreactnativesampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-sentryreactnativesample-sentryreactnativesampleTests.release.xcconfig"; path = "Target Support Files/Pods-sentryreactnativesample-sentryreactnativesampleTests/Pods-sentryreactnativesample-sentryreactnativesampleTests.release.xcconfig"; sourceTree = ""; }; 916F55A329D66130A4DFF319 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = sentryreactnativesample/PrivacyInfo.xcprivacy; sourceTree = ""; }; + AE3C91AC2E3BB98B00DC7C20 /* SentryNativeInitializer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryNativeInitializer.h; sourceTree = ""; }; + AE3C91AD2E3BB9B000DC7C20 /* SentryNativeInitializer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryNativeInitializer.m; sourceTree = ""; }; AFC2BCCFBDE2DC231B5C04E5 /* libPods-sentryreactnativesample-sentryreactnativesampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-sentryreactnativesample-sentryreactnativesampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; CA08EE94AE203638B8C8B74B /* libPods-sentryreactnativesample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-sentryreactnativesample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -94,6 +97,8 @@ 13B07FAE1A68108700A75B9A /* sentryreactnativesample */ = { isa = PBXGroup; children = ( + AE3C91AD2E3BB9B000DC7C20 /* SentryNativeInitializer.m */, + AE3C91AC2E3BB98B00DC7C20 /* SentryNativeInitializer.h */, 338BBBC72B614FA10035844C /* NativePlatformSampleModule.h */, 338BBBC62B614FA10035844C /* NativePlatformSampleModule.mm */, 33E2D62929A7719600B5042B /* RCTAssetsModule.h */, @@ -427,6 +432,7 @@ 13B07FC11A68108700A75B9A /* main.m in Sources */, 338BBBC82B614FA10035844C /* NativePlatformSampleModule.mm in Sources */, 33E2D62A29A7719600B5042B /* RCTAssetsModule.m in Sources */, + AE3C91AE2E3BB9B400DC7C20 /* SentryNativeInitializer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/samples/react-native/ios/sentryreactnativesample/AppDelegate.mm b/samples/react-native/ios/sentryreactnativesample/AppDelegate.mm index aedb222cbc..0df8934ff1 100644 --- a/samples/react-native/ios/sentryreactnativesample/AppDelegate.mm +++ b/samples/react-native/ios/sentryreactnativesample/AppDelegate.mm @@ -13,8 +13,7 @@ # import #endif -#import -#import +#import "SentryNativeInitializer.h" @interface AppDelegate () { @@ -23,44 +22,12 @@ @implementation AppDelegate -- (void)initializeSentry -{ - [SentrySDK startWithConfigureOptions:^(SentryOptions *options) { - // Only options set here will apply to the iOS SDK - // Options from JS are not passed to the iOS SDK when initialized manually - options.dsn = @"https://1df17bd4e543fdb31351dee1768bb679@o447951.ingest.sentry.io/5428561"; - options.debug = YES; // Enabled debug when first installing is always helpful - - options.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) { - NSLog(@"Unhandled JS Exception"); - return nil; - } - - return event; - }; - - // Enable the App start and Frames tracking measurements - // If this is disabled the app start and frames tracking - // won't be passed from native to JS transactions - PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = true; -#if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST - PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode = true; -#endif - }]; -} - - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // When the native init is enabled the `autoInitializeNativeSdk` // in JS has to be set to `false` - // [self initializeSentry]; + // [SentryNativeInitializer initializeSentry]; self.reactNativeFactory = [[RCTReactNativeFactory alloc] initWithDelegate:self]; self.dependencyProvider = [RCTAppDependencyProvider new];