Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
36 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
2cc92f2
feat: Read `sentry.options.json` during cocoa init
krystofwoldrich Jan 14, 2025
592b903
Update CHANGELOG.md
krystofwoldrich Jan 15, 2025
a2717bd
remove global processor
krystofwoldrich Jan 15, 2025
5ecd6b2
Merge remote-tracking branch 'origin/kw-add-read-ios-options-file' in…
krystofwoldrich Jan 15, 2025
890133a
Update code docs for start methods
krystofwoldrich Jan 15, 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
4ac4896
Merge branch 'kw-add-react-native-ios-manual-init' into kw-add-read-i…
krystofwoldrich Jan 21, 2025
1baac18
add tests
krystofwoldrich Jan 21, 2025
f92edd9
remove unavailable and unused global events processors import
krystofwoldrich Jan 21, 2025
2662dbd
Merge branch 'kw-add-react-native-ios-manual-init' into kw-add-read-i…
krystofwoldrich Jan 21, 2025
7baad41
fix c-format
krystofwoldrich Jan 21, 2025
6d81a10
remove old sentry-cocoa init code
krystofwoldrich Jan 22, 2025
92c32e9
Merge remote-tracking branch 'origin/capture-app-start-errors' into k…
krystofwoldrich Jan 22, 2025
dcfdc6c
Merge remote-tracking branch 'origin/kw-add-read-ios-options-file' in…
krystofwoldrich Jan 22, 2025
7eb1786
fix copy options json only if destination set and file exists
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@

- 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))
- Add initialization using `sentry.options.json` for Apple platforms ([#4447](https://github.com/getsentry/sentry-react-native/pull/4447))

### Internal

- Initialize `RNSentryTimeToDisplay` during native module `init` on iOS ([#4443](https://github.com/getsentry/sentry-react-native/pull/4443))
- Extract iOS native initialization to standalone structures ([#4444](https://github.com/getsentry/sentry-react-native/pull/4444))

### Dependencies

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 @@ -25,6 +25,9 @@
332D33482CDBDC7300547D76 /* RNSentry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentry.h; path = ../ios/RNSentry.h; sourceTree = SOURCE_ROOT; };
332D33492CDCC8E100547D76 /* RNSentryTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSentryTests.h; sourceTree = "<group>"; };
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 Down Expand Up @@ -116,6 +119,9 @@
33AFE0122B8F319000AAB120 /* RNSentry */ = {
isa = PBXGroup;
children = (
333B58AF2D36A7FD000F8D04 /* RNSentrySDK.h */,
333B58A92D35BB2D000F8D04 /* RNSentryStart+Test.h */,
333B58A82D35BA93000F8D04 /* RNSentryStart.h */,
3380C6C02CDEC56B0018B9B6 /* Replay */,
332D33482CDBDC7300547D76 /* RNSentry.h */,
3360843A2C32E3A8008CC412 /* RNSentryReplayBreadcrumbConverter.h */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#import "RNSentryStart.h"

@interface
RNSentryStart (Test)

+ (void)setEventOriginTag:(SentryEvent *)event;

@end

Large diffs are not rendered by default.

8 changes: 3 additions & 5 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 All @@ -20,11 +23,6 @@ SentrySDK (Private)

@interface RNSentry : RCTEventEmitter <RCTBridgeModule>

- (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)options
error:(NSError *_Nullable *_Nonnull)errorPointer;

- (void)setEventOriginTag:(SentryEvent *)event;

- (NSDictionary *_Nonnull)fetchNativeStackFramesBy:(NSArray<NSNumber *> *)instructionsAddr
symbolicate:(SymbolicateCallbackType)symbolicate;

Expand Down
181 changes: 10 additions & 171 deletions packages/core/ios/RNSentry.mm
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
# import "RNSentryRNSScreen.h"
#endif

#import "RNSentryStart.h"
#import "RNSentryVersion.h"

@interface
Expand All @@ -63,7 +64,6 @@ + (void)storeEnvelope:(SentryEnvelope *)envelope;
static bool hasFetchedAppStart;

@implementation RNSentry {
bool sentHybridSdkDidBecomeActive;
bool hasListeners;
RNSentryTimeToDisplay *_timeToDisplay;
}
Expand All @@ -78,6 +78,14 @@ + (BOOL)requiresMainQueueSetup
return YES;
}

- (instancetype)init
{
if (self = [super init]) {
_timeToDisplay = [[RNSentryTimeToDisplay alloc] init];
}
return self;
}

RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(initNativeSdk
Expand All @@ -86,183 +94,14 @@ + (BOOL)requiresMainQueueSetup
: (RCTPromiseRejectBlock)reject)
{
NSError *error = nil;
SentryOptions *sentryOptions = [self createOptionsWithDictionary:options error:&error];
[RNSentryStart startWithOptions:options 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];

[SentrySDK startWithOptions:sentryOptions];

#if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST
BOOL appIsActive =
[[UIApplication sharedApplication] applicationState] == UIApplicationStateActive;
#else
BOOL appIsActive = [[NSApplication sharedApplication] isActive];
#endif

// 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)) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"SentryHybridSdkDidBecomeActive"
object:nil];

sentHybridSdkDidBecomeActive = true;
}

#if SENTRY_TARGET_REPLAY_SUPPORTED
[RNSentryReplay postInit];
#endif

resolve(@YES);
}

- (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;
}

[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"];

_timeToDisplay = [[RNSentryTimeToDisplay alloc] init];

#if SENTRY_TARGET_REPLAY_SUPPORTED
[RNSentryReplay updateOptions:mutableOptions];
#endif

SentryOptions *sentryOptions = [[SentryOptions alloc] 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) {
NSMutableArray *integrations = sentryOptions.integrations.mutableCopy;
[integrations removeObject:@"SentryCrashIntegration"];
sentryOptions.integrations = integrations;
}
}

// 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;
}
}
}

// 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;

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) {
NSString *sdkName = event.sdk[@"name"];

// If the event is from react native, it gets set
// there and we do not handle it here.
if ([sdkName isEqual:NATIVE_SDK_NAME]) {
[self setEventEnvironmentTag:event origin:@"ios" environment:@"native"];
}
}
}

- (void)setEventEnvironmentTag:(SentryEvent *)event
origin:(NSString *)origin
environment:(NSString *)environment
{
NSMutableDictionary *newTags = [NSMutableDictionary new];

if (nil != event.tags && [event.tags count] > 0) {
[newTags addEntriesFromDictionary:event.tags];
}
if (nil != origin) {
[newTags setValue:origin forKey:@"event.origin"];
}
if (nil != environment) {
[newTags setValue:environment forKey:@"event.environment"];
}

event.tags = newTags;
}

RCT_EXPORT_METHOD(initNativeReactNavigationNewFrameTracking
: (RCTPromiseResolveBlock)resolve rejecter
: (RCTPromiseRejectBlock)reject)
Expand Down
30 changes: 30 additions & 0 deletions packages/core/ios/RNSentrySDK.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#import <Sentry/Sentry.h>

@interface RNSentrySDK : NSObject
SENTRY_NO_INIT

/**
* @experimental
* Inits and configures Sentry for React Native applications using `sentry.options.json`
* configuration file.
*
* @discussion Call this method on the main thread. When calling it from a background thread, the
* SDK starts on the main thread async.
*/
+ (void)start;

/**
* @experimental
* Inits and configures Sentry for React Native applicationsusing `sentry.options.json`
* configuration file and `configureOptions` callback.
*
* The `configureOptions` callback can overwrite the config file options
* and add non-serializable items to the options object.
*
* @discussion Call this method on the main thread. When calling it from a background thread, the
* SDK starts on the main thread async.
*/
+ (void)startWithConfigureOptions:(void (^)(SentryOptions *options))configureOptions
NS_SWIFT_NAME(start(configureOptions:));

@end
60 changes: 60 additions & 0 deletions packages/core/ios/RNSentrySDK.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#import "RNSentrySDK.h"
#import "RNSentryStart.h"

static NSString *SENTRY_OPTIONS_RESOURCE_NAME = @"sentry.options";
static NSString *SENTRY_OPTIONS_RESOURCE_TYPE = @"json";

@implementation RNSentrySDK

+ (void)start
{
[self startWithConfigureOptions:nil];
}

+ (void)startWithConfigureOptions:(void (^)(SentryOptions *options))configureOptions
{
NSError *readError = nil;
NSError *parseError = nil;
NSError *optionsError = nil;

NSString *path = [[NSBundle mainBundle] pathForResource:SENTRY_OPTIONS_RESOURCE_NAME
ofType:SENTRY_OPTIONS_RESOURCE_TYPE];
NSData *content = [NSData dataWithContentsOfFile:path options:0 error:&readError];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:content
options:0
error:&parseError];

if (readError != nil) {
NSLog(@"[RNSentry] Failed to load options from %@, with error: %@", path,
readError.localizedDescription);
}

if (parseError != nil) {
NSLog(@"[RNSentry] Failed to parse JSON from %@, with error: %@", path,
parseError.localizedDescription);
}

SentryOptions *options = nil;
if (dict != nil) {
options = [RNSentryStart createOptionsWithDictionary:dict error:&optionsError];
}

if (optionsError != nil) {
NSLog(@"[RNSentry] Failed to parse options from %@, with error: %@", path,
optionsError.localizedDescription);
}

if (options == nil) {
// Fallback in case that options file could not be parsed.
options = [[SentryOptions alloc] init];
}

[RNSentryStart updateWithReactDefaults:options];
if (configureOptions != nil) {
configureOptions(options);
}
[RNSentryStart updateWithReactFinals:options];
[RNSentryStart startWithOptions:options];
}

@end
Loading
Loading