Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
- Fix compatibility with `react-native-legal` ([#5253](https://github.com/getsentry/sentry-react-native/pull/5253))
- The licenses json file is correctly generated and placed into the `res/` folder now
- Handle missing shouldAddToIgnoreList callback in Metro ([#5260](https://github.com/getsentry/sentry-react-native/pull/5260))
- Overrides the default Cocoa SDK behavior that disables Session Replay on iOS 26.0 ([#5268](https://github.com/getsentry/sentry-react-native/pull/5268))
- If you are using Apple's Liquid Glass we recommend that you disable Session Replay on iOS to prevent potential PII leaks (see [sentry-cocoa 8.57.0 release note warning](https://github.com/getsentry/sentry-cocoa/releases/tag/8.57.0))

### Dependencies

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -736,4 +736,129 @@ - (void)testIgnoreErrorsRegexAndStringBothWork
XCTAssertNotNil(result3, @"Event with non-matching error should not be dropped");
}

- (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentDefault
{
RNSentry *rnSentry = [[RNSentry alloc] init];
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://[email protected]/123456",
};
SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary
error:&error];

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");

id experimentalOptions = [actualOptions valueForKey:@"experimental"];
XCTAssertNotNil(experimentalOptions, @"Experimental options should not be nil");

BOOL enableUnhandledCPPExceptions =
[[experimentalOptions valueForKey:@"enableSessionReplayInUnreliableEnvironment"] boolValue];
XCTAssertFalse(enableUnhandledCPPExceptions,
@"enableSessionReplayInUnreliableEnvironment should be disabled");
}

- (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentWithErrorSampleRate
{
RNSentry *rnSentry = [[RNSentry alloc] init];
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://[email protected]/123456",
@"replaysOnErrorSampleRate" : @1.0,
@"replaysSessionSampleRate" : @0
};
SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary
error:&error];

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");

id experimentalOptions = [actualOptions valueForKey:@"experimental"];
XCTAssertNotNil(experimentalOptions, @"Experimental options should not be nil");

BOOL enableUnhandledCPPExceptions =
[[experimentalOptions valueForKey:@"enableSessionReplayInUnreliableEnvironment"] boolValue];
XCTAssertTrue(enableUnhandledCPPExceptions,
@"enableSessionReplayInUnreliableEnvironment should be enabled");
}

- (void)
testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentWithSessionSampleRate
{
RNSentry *rnSentry = [[RNSentry alloc] init];
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://[email protected]/123456",
@"replaysOnErrorSampleRate" : @0.0,
@"replaysSessionSampleRate" : @0.1
};
SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary
error:&error];

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");

id experimentalOptions = [actualOptions valueForKey:@"experimental"];
XCTAssertNotNil(experimentalOptions, @"Experimental options should not be nil");

BOOL enableUnhandledCPPExceptions =
[[experimentalOptions valueForKey:@"enableSessionReplayInUnreliableEnvironment"] boolValue];
XCTAssertTrue(enableUnhandledCPPExceptions,
@"enableSessionReplayInUnreliableEnvironment should be enabled");
}

- (void)
testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentWithSessionSampleRates
{
RNSentry *rnSentry = [[RNSentry alloc] init];
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://[email protected]/123456",
@"replaysOnErrorSampleRate" : @1.0,
@"replaysSessionSampleRate" : @0.1
};
SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary
error:&error];

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");

id experimentalOptions = [actualOptions valueForKey:@"experimental"];
XCTAssertNotNil(experimentalOptions, @"Experimental options should not be nil");

BOOL enableUnhandledCPPExceptions =
[[experimentalOptions valueForKey:@"enableSessionReplayInUnreliableEnvironment"] boolValue];
XCTAssertTrue(enableUnhandledCPPExceptions,
@"enableSessionReplayInUnreliableEnvironment should be enabled");
}

- (void)testCreateOptionsWithDictionaryEnableSessionReplayInUnreliableEnvironmentDisabled
{
RNSentry *rnSentry = [[RNSentry alloc] init];
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://[email protected]/123456",
@"replaysOnErrorSampleRate" : @0,
@"replaysSessionSampleRate" : @0
};
SentryOptions *actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary
error:&error];

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");

id experimentalOptions = [actualOptions valueForKey:@"experimental"];
XCTAssertNotNil(experimentalOptions, @"Experimental options should not be nil");

BOOL enableUnhandledCPPExceptions =
[[experimentalOptions valueForKey:@"enableSessionReplayInUnreliableEnvironment"] boolValue];
XCTAssertFalse(enableUnhandledCPPExceptions,
@"enableSessionReplayInUnreliableEnvironment should be disabled");
}

@end
10 changes: 9 additions & 1 deletion packages/core/ios/RNSentry.mm
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,10 @@ - (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)
[mutableOptions removeObjectForKey:@"enableTracing"];

#if SENTRY_TARGET_REPLAY_SUPPORTED
[RNSentryReplay updateOptions:mutableOptions];
BOOL isSessionReplayEnabled = [RNSentryReplay updateOptions:mutableOptions];
#else
// Defaulting to false for unsupported targets
BOOL isSessionReplayEnabled = NO;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: There might be value in adding a comment here for future maintainers to understand why it defaults to NO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added with 46986c4

#endif

SentryOptions *sentryOptions = [SentryOptionsInternal initWithDict:mutableOptions
Expand Down Expand Up @@ -315,6 +318,11 @@ - (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)
sentryOptions:sentryOptions];
}

if (isSessionReplayEnabled) {
[RNSentryExperimentalOptions setEnableSessionReplayInUnreliableEnvironment:YES
sentryOptions:sentryOptions];
}

return sentryOptions;
}

Expand Down
9 changes: 9 additions & 0 deletions packages/core/ios/RNSentryExperimentalOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (void)setEnableLogs:(BOOL)enabled sentryOptions:(SentryOptions *)sentryOptions;

/**
* Sets the enableSessionReplayInUnreliableEnvironment experimental option on SentryOptions
* @param sentryOptions The SentryOptions instance to configure
* @param enabled Whether enableSessionReplayInUnreliableEnvironment from sentry Cocoa should be
* enabled
*/
+ (void)setEnableSessionReplayInUnreliableEnvironment:(BOOL)enabled
sentryOptions:(SentryOptions *)sentryOptions;

@end

NS_ASSUME_NONNULL_END
9 changes: 9 additions & 0 deletions packages/core/ios/RNSentryExperimentalOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,13 @@ + (void)setEnableLogs:(BOOL)enabled sentryOptions:(SentryOptions *)sentryOptions
sentryOptions.experimental.enableLogs = enabled;
}

+ (void)setEnableSessionReplayInUnreliableEnvironment:(BOOL)enabled
sentryOptions:(SentryOptions *)sentryOptions
{
if (sentryOptions == nil) {
return;
}
sentryOptions.experimental.enableSessionReplayInUnreliableEnvironment = enabled;
}

@end
6 changes: 5 additions & 1 deletion packages/core/ios/RNSentryReplay.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@

@interface RNSentryReplay : NSObject

+ (void)updateOptions:(NSMutableDictionary *)options;
/**
* Updates the session replay options
* @return true when session replay is enabled
*/
+ (BOOL)updateOptions:(NSMutableDictionary *)options;

+ (void)postInit;

Expand Down
16 changes: 10 additions & 6 deletions packages/core/ios/RNSentryReplay.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
@implementation RNSentryReplay {
}

+ (void)updateOptions:(NSMutableDictionary *)options
+ (BOOL)updateOptions:(NSMutableDictionary *)options
{
if (options[@"replaysSessionSampleRate"] == nil
&& options[@"replaysOnErrorSampleRate"] == nil) {
NSNumber *sessionSampleRate = options[@"replaysSessionSampleRate"];
NSNumber *errorSampleRate = options[@"replaysOnErrorSampleRate"];

if (sessionSampleRate == nil && errorSampleRate == nil) {
NSLog(@"Session replay disabled via configuration");
return;
return NO;
}

NSLog(@"Setting up session replay");
Expand All @@ -26,8 +28,8 @@ + (void)updateOptions:(NSMutableDictionary *)options
NSString *qualityString = options[@"replaysSessionQuality"];

[options setValue:@{
@"sessionSampleRate" : options[@"replaysSessionSampleRate"] ?: [NSNull null],
@"errorSampleRate" : options[@"replaysOnErrorSampleRate"] ?: [NSNull null],
@"sessionSampleRate" : sessionSampleRate ?: [NSNull null],
@"errorSampleRate" : errorSampleRate ?: [NSNull null],
@"quality" : @([RNSentryReplayQuality parseReplayQuality:qualityString]),
@"maskAllImages" : replayOptions[@"maskAllImages"] ?: [NSNull null],
@"maskAllText" : replayOptions[@"maskAllText"] ?: [NSNull null],
Expand All @@ -38,6 +40,8 @@ + (void)updateOptions:(NSMutableDictionary *)options
@ { @"name" : REACT_NATIVE_SDK_NAME, @"version" : REACT_NATIVE_SDK_PACKAGE_VERSION }
}
forKey:@"sessionReplay"];
return (errorSampleRate != nil && [errorSampleRate doubleValue] > 0)
|| (sessionSampleRate != nil && [sessionSampleRate doubleValue] > 0);
}

+ (NSArray *_Nonnull)getReplayRNRedactClasses:(NSDictionary *_Nullable)replayOptions
Expand Down
Loading