Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Copy link
Member

Choose a reason for hiding this comment

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

m: We should add a reference to the warning in https://github.com/getsentry/sentry-cocoa/releases/tag/8.57.0 so that users can get the full context.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Added with 6b6b75b


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

#if SENTRY_TARGET_REPLAY_SUPPORTED
[RNSentryReplay updateOptions:mutableOptions];
BOOL isSessionReplayEnabled = [RNSentryReplay updateOptions:mutableOptions];
#else
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 +317,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