Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(experimental): Add native startWithConfigureOptions for Apple platforms #4444

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4328298
chore: Initialize `RNSentryTimeToDisplay` during native module `init`…
krystofwoldrich Jan 14, 2025
27d76de
ref(ios): Extract Cocoa SDK init into standalone file
krystofwoldrich Jan 14, 2025
5a28c03
fix lint
krystofwoldrich Jan 14, 2025
8b67bf1
rename rnsentrysdk to rnsentrystart
krystofwoldrich Jan 14, 2025
05cef29
rename class name also to rnsentrystart, fix tests
krystofwoldrich Jan 14, 2025
778478f
explicitly import only used classes
krystofwoldrich Jan 14, 2025
3d3d0b4
fix call cocoa start not itself
krystofwoldrich Jan 14, 2025
e2e40fa
also extract didBecomeActive notification
krystofwoldrich Jan 14, 2025
83106a7
add explanation comment to native crash handling
krystofwoldrich Jan 14, 2025
742f28c
explain notification purpose
krystofwoldrich Jan 14, 2025
4897aa3
fix sdk name
krystofwoldrich Jan 14, 2025
d37ebdd
fix native sdk not react native package
krystofwoldrich Jan 14, 2025
13a0b89
fix lint sentry start
krystofwoldrich Jan 14, 2025
d7270b1
add changelog
krystofwoldrich Jan 14, 2025
580590b
feat(experimental): Add native `startWithConfigureOptions` for Apple …
krystofwoldrich Jan 14, 2025
2d021e8
Merge branch 'main' into kw-ref-ios-sdk-init
krystofwoldrich Jan 20, 2025
cfc6293
fix lint
krystofwoldrich Jan 20, 2025
f2a041e
remove unused rnSentry
krystofwoldrich Jan 20, 2025
fc4ff7e
fix changelog
krystofwoldrich Jan 20, 2025
879044f
Merge branch 'kw-ref-ios-sdk-init' into kw-add-react-native-ios-manua…
krystofwoldrich Jan 20, 2025
dec9869
wip! add manual init tests
krystofwoldrich Jan 20, 2025
22e88c7
add tests
krystofwoldrich Jan 21, 2025
f92edd9
remove unavailable and unused global events processors import
krystofwoldrich Jan 21, 2025
6c1ca89
Merge branch 'capture-app-start-errors' into kw-add-react-native-ios-…
krystofwoldrich Jan 22, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

- Rename `navigation.processing` span to more expressive `Navigation dispatch to screen A mounted/navigation cancelled` ([#4423](https://github.com/getsentry/sentry-react-native/pull/4423))
- Add RN SDK package to `sdk.packages` for Cocoa ([#4381](https://github.com/getsentry/sentry-react-native/pull/4381))
- Add experimental version of `startWithConfigureOptions` for Apple platforms ([#4444](https://github.com/getsentry/sentry-react-native/pull/4444))

### Internal

Expand Down
2 changes: 1 addition & 1 deletion packages/core/RNSentry.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Pod::Spec.new do |s|
s.preserve_paths = '*.js'

s.source_files = 'ios/**/*.{h,m,mm}'
s.public_header_files = 'ios/RNSentry.h'
s.public_header_files = 'ios/RNSentry.h', 'ios/RNSentrySDK.h'

s.compiler_flags = other_cflags

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
336084392C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 336084382C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift */; };
3380C6C42CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3380C6C32CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift */; };
33958C692BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33958C682BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m */; };
339C6C3C2D3EB25100CA72ED /* RNSentryStartTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339C6C3B2D3EB23B00CA72ED /* RNSentryStartTests.swift */; };
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */; };
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */; };
33F58AD02977037D008F60EA /* RNSentryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 33F58ACF2977037D008F60EA /* RNSentryTests.mm */; };
Expand All @@ -27,6 +28,7 @@
332D334A2CDCC8EB00547D76 /* RNSentryCocoaTesterTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNSentryCocoaTesterTests-Bridging-Header.h"; sourceTree = "<group>"; };
333B58A82D35BA93000F8D04 /* RNSentryStart.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentryStart.h; path = ../ios/RNSentryStart.h; sourceTree = SOURCE_ROOT; };
333B58A92D35BB2D000F8D04 /* RNSentryStart+Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "RNSentryStart+Test.h"; path = "RNSentryCocoaTesterTests/RNSentryStart+Test.h"; sourceTree = SOURCE_ROOT; };
333B58AF2D36A7FD000F8D04 /* RNSentrySDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentrySDK.h; path = ../ios/RNSentrySDK.h; sourceTree = SOURCE_ROOT; };
336084382C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RNSentryReplayBreadcrumbConverterTests.swift; sourceTree = "<group>"; };
3360843A2C32E3A8008CC412 /* RNSentryReplayBreadcrumbConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryReplayBreadcrumbConverter.h; path = ../ios/RNSentryReplayBreadcrumbConverter.h; sourceTree = "<group>"; };
3360843C2C340C76008CC412 /* RNSentryBreadcrumbTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryBreadcrumbTests.swift; sourceTree = "<group>"; };
Expand All @@ -37,6 +39,8 @@
338739072A7D7D2800950DDD /* RNSentryReplay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentryReplay.h; path = ../ios/RNSentryReplay.h; sourceTree = "<group>"; };
33958C672BFCEF5A00AD1FB6 /* RNSentryOnDrawReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryOnDrawReporter.h; path = ../ios/RNSentryOnDrawReporter.h; sourceTree = "<group>"; };
33958C682BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryOnDrawReporterTests.m; sourceTree = "<group>"; };
339C6C3B2D3EB23B00CA72ED /* RNSentryStartTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryStartTests.swift; sourceTree = "<group>"; };
339C6C3D2D3FA04D00CA72ED /* RNSentryVersion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentryVersion.h; path = ../ios/RNSentryVersion.h; sourceTree = SOURCE_ROOT; };
33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryFramesTrackerListenerTests.m; sourceTree = "<group>"; };
33AFDFEE2B8D14C200AAB120 /* RNSentryFramesTrackerListenerTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSentryFramesTrackerListenerTests.h; sourceTree = "<group>"; };
33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryDependencyContainerTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -90,6 +94,7 @@
3360899029524164007C7730 /* RNSentryCocoaTesterTests */ = {
isa = PBXGroup;
children = (
339C6C3B2D3EB23B00CA72ED /* RNSentryStartTests.swift */,
332D334A2CDCC8EB00547D76 /* RNSentryCocoaTesterTests-Bridging-Header.h */,
332D33492CDCC8E100547D76 /* RNSentryTests.h */,
336084382C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift */,
Expand Down Expand Up @@ -118,6 +123,8 @@
33AFE0122B8F319000AAB120 /* RNSentry */ = {
isa = PBXGroup;
children = (
339C6C3D2D3FA04D00CA72ED /* RNSentryVersion.h */,
333B58AF2D36A7FD000F8D04 /* RNSentrySDK.h */,
333B58A92D35BB2D000F8D04 /* RNSentryStart+Test.h */,
333B58A82D35BA93000F8D04 /* RNSentryStart.h */,
3380C6C02CDEC56B0018B9B6 /* Replay */,
Expand Down Expand Up @@ -243,6 +250,7 @@
files = (
AEFB00422CC90C4B00EC8A9A /* RNSentryBreadcrumbTests.swift in Sources */,
332D33472CDBDBB600547D76 /* RNSentryReplayOptionsTests.swift in Sources */,
339C6C3C2D3EB25100CA72ED /* RNSentryStartTests.swift in Sources */,
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */,
336084392C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift in Sources */,
33F58AD02977037D008F60EA /* RNSentryTests.mm in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
#import "RNSentryReplayBreadcrumbConverter.h"
#import "RNSentryReplayMask.h"
#import "RNSentryReplayUnmask.h"
#import "RNSentrySDK.h"
#import "RNSentryStart.h"
#import "RNSentryVersion.h"
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import XCTest

final class RNSentryStartTests: XCTestCase {

func testStartDoesNotThrowWithoutConfigure() {
RNSentrySDK.start(configureOptions: nil)
}

func assertReactDefaults(_ actualOptions: Options?) {
XCTAssertFalse(actualOptions!.enableCaptureFailedRequests)
XCTAssertNil(actualOptions!.tracesSampleRate)
XCTAssertNil(actualOptions!.tracesSampler)
XCTAssertFalse(actualOptions!.enableTracing)
}

func testStartSetsReactDeafults() {
var actualOptions: Options?

RNSentrySDK.start { options in
actualOptions = options
}

XCTAssertNotNil(actualOptions, "start have not provided default options or have not executed configure callback")
assertReactDefaults(actualOptions)
}

func testAutoStartSetsReactDefaults() throws {
try startFromRN(options: [
"dsn": "https://[email protected]/123456"
])

let actualOptions = PrivateSentrySDKOnly.options
assertReactDefaults(actualOptions)
}

func testStartEnablesHybridTracing() throws {
let testCases: [() throws -> Void] = [
{
RNSentrySDK.start { options in
options.dsn = "https://[email protected]/123456"
}
},
{
try self.startFromRN(options: [
"dsn": "https://[email protected]/123456"
])
},
{
RNSentrySDK.start { options in
options.dsn = "https://[email protected]/123456"
options.enableAutoPerformanceTracing = true
}
},
{
try self.startFromRN(options: [
"dsn": "https://[email protected]/123456",
"enableAutoPerformanceTracing": true
])
}
]

// Test each implementation
for startMethod in testCases {
try startMethod()

let actualOptions = PrivateSentrySDKOnly.options

XCTAssertTrue(PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode)
XCTAssertTrue(PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode)
}
}

func testStartDisablesHybridTracing() throws {
let testCases: [() throws -> Void] = [
{
RNSentrySDK.start { options in
options.dsn = "https://[email protected]/123456"
options.enableAutoPerformanceTracing = false
}
},
{
try self.startFromRN(options: [
"dsn": "https://[email protected]/123456",
"enableAutoPerformanceTracing": false
])
}
]

for startMethod in testCases {
try startMethod()

let actualOptions = PrivateSentrySDKOnly.options

XCTAssertFalse(PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode)
XCTAssertFalse(PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode)
}
}

func testStartIgnoresUnhandledJsExceptions() throws {
let testCases: [() throws -> Void] = [
{
RNSentrySDK.start { options in
options.dsn = "https://[email protected]/123456"
}
},
{
try self.startFromRN(options: [
"dsn": "https://[email protected]/123456"
])
}
]

for startMethod in testCases {
try startMethod()

let actualOptions = PrivateSentrySDKOnly.options

let actualEvent = actualOptions.beforeSend!(createUnhandledJsExceptionEvent())

XCTAssertNil(actualEvent)
}
}

func testStartSetsNativeEventOrigin() throws {
let testCases: [() throws -> Void] = [
{
RNSentrySDK.start { options in
options.dsn = "https://[email protected]/123456"
}
},
{
try self.startFromRN(options: [
"dsn": "https://[email protected]/123456"
])
}
]

for startMethod in testCases {
try startMethod()

let actualOptions = PrivateSentrySDKOnly.options

let actualEvent = actualOptions.beforeSend!(createNativeEvent())

XCTAssertNotNil(actualEvent)
XCTAssertNotNil(actualEvent!.tags)
XCTAssertEqual(actualEvent!.tags!["event.origin"], "ios")
XCTAssertEqual(actualEvent!.tags!["event.environment"], "native")
}
}

func testStartDoesNotOverwriteUserBeforeSend() {
var executed = false

RNSentrySDK.start { options in
options.dsn = "https://[email protected]/123456"
options.beforeSend = { event in
executed = true
return event
}
}

PrivateSentrySDKOnly.options.beforeSend!(genericEvent())

XCTAssertTrue(executed)
}

func testStartSetsHybridSdkName() throws {
let testCases: [() throws -> Void] = [
{
RNSentrySDK.start { options in
options.dsn = "https://[email protected]/123456"
}
},
{
try self.startFromRN(options: [
"dsn": "https://[email protected]/123456"
])
}
]

for startMethod in testCases {
try startMethod()

let actualEvent = captuteTestEvent()

XCTAssertNotNil(actualEvent)
XCTAssertNotNil(actualEvent!.sdk)
XCTAssertEqual(actualEvent!.sdk!["name"] as! String, NATIVE_SDK_NAME)

let packages = actualEvent!.sdk!["packages"] as! [[String: String]]
let reactPackage = packages.first { $0["name"] == REACT_NATIVE_SDK_PACKAGE_NAME }

XCTAssertNotNil(reactPackage)
XCTAssertEqual(reactPackage!["name"], REACT_NATIVE_SDK_PACKAGE_NAME)
XCTAssertEqual(reactPackage!["version"], REACT_NATIVE_SDK_PACKAGE_VERSION)
}
}

func startFromRN(options: [String: Any]) throws {
var error: NSError?
RNSentryStart.start(options: options, error: &error)

if let error = error {
throw error
}
}

func createUnhandledJsExceptionEvent() -> Event {
let event = Event()
event.exceptions = []
event.exceptions!.append(Exception(value: "Test", type: "Unhandled JS Exception: undefined is not a function"))
return event
}

func createNativeEvent() -> Event {
let event = Event()
event.sdk = [
"name": NATIVE_SDK_NAME,
"version": "1.2.3"
]
return event
}

func genericEvent() -> Event {
return Event()
}

func captuteTestEvent() -> Event? {
var actualEvent: Event?

// This is the closest to the sent event we can get using the actual Sentry start method
let originalBeforeSend = PrivateSentrySDKOnly.options.beforeSend
PrivateSentrySDKOnly.options.beforeSend = { event in
if let originalBeforeSend = originalBeforeSend {
let processedEvent = originalBeforeSend(event)
actualEvent = processedEvent
return processedEvent
}
actualEvent = event
return event
}

SentrySDK.capture(message: "Test")

return actualEvent
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#import "RNSentryStart+Test.h"
#import <OCMock/OCMock.h>
#import <RNSentry/RNSentry.h>
#import <Sentry/PrivateSentrySDKOnly.h>
#import <Sentry/SentryDebugImageProvider+HybridSDKs.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
Expand All @@ -12,7 +13,7 @@ @interface RNSentryInitNativeSdkTests : XCTestCase

@implementation RNSentryInitNativeSdkTests

- (void)testCreateOptionsWithDictionaryRemovesPerformanceProperties
- (void)testStartWithDictionaryRemovesPerformanceProperties
{
NSError *error = nil;

Expand All @@ -25,9 +26,8 @@ - (void)testCreateOptionsWithDictionaryRemovesPerformanceProperties
, @"enableTracing" : @YES,
}
;
SentryOptions *actualOptions =
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];

[RNSentryStart startWithOptions:mockedReactNativeDictionary error:&error];
SentryOptions *actualOptions = PrivateSentrySDKOnly.options;
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");
XCTAssertNotNil(
Expand All @@ -45,8 +45,8 @@ - (void)testCaptureFailedRequestsIsDisabled
NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://[email protected]/123456",
};
SentryOptions *actualOptions =
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];
[RNSentryStart startWithOptions:mockedReactNativeDictionary error:&error];
SentryOptions *actualOptions = PrivateSentrySDKOnly.options;

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");
Expand Down
3 changes: 3 additions & 0 deletions packages/core/ios/RNSentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#import <Sentry/SentryDebugImageProvider.h>
#import <Sentry/SentryOptions.h>

// This import exposes public RNSentrySDK start
#import "RNSentrySDK.h"

typedef int (*SymbolicateCallbackType)(const void *, Dl_info *);

@interface
Expand Down
4 changes: 1 addition & 3 deletions packages/core/ios/RNSentry.mm
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,11 @@ - (instancetype)init
: (RCTPromiseRejectBlock)reject)
{
NSError *error = nil;
SentryOptions *sentryOptions = [RNSentryStart createOptionsWithDictionary:options error:&error];
[RNSentryStart startWithOptions:options error:&error];
if (error != nil) {
reject(@"SentryReactNative", error.localizedDescription, error);
return;
}

[RNSentryStart startWithOptions:sentryOptions];
resolve(@YES);
}

Expand Down
Loading
Loading