diff --git a/CHANGELOG.md b/CHANGELOG.md index 9169d6117f..9f299bdd56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Fixes - Enable native logs on Android when `enableLogs` is set ([#6967](https://github.com/getsentry/sentry-react-native/pull/6967)) +- Honor `` in iOS error screenshots ([#6974](https://github.com/getsentry/sentry-react-native/pull/6974)) ## 8.33.0 diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryStartTests.swift b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryStartTests.swift index 1e875e74fc..a48974b567 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryStartTests.swift +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryStartTests.swift @@ -225,6 +225,44 @@ final class RNSentryStartTests: XCTestCase { XCTAssertTrue(actualOptions.screenshot.maskAllImages) } + func testScreenshotMasksSentryMaskComponent() throws { + try startFromRN(options: [ + "dsn": "https://abcd@efgh.ingest.sentry.io/123456", + "attachScreenshot": true, + "screenshot": [ + "maskAllText": false, + "maskAllImages": false + ] + ]) + + let maskedViewClasses = SentrySDK.internal.options.screenshot.maskedViewClasses + XCTAssertTrue(maskedViewClasses.contains { $0 == RNSentryReplay.getMaskClass() }) + } + + func testScreenshotMasksSentryMaskComponentWithoutScreenshotOptions() throws { + try startFromRN(options: [ + "dsn": "https://abcd@efgh.ingest.sentry.io/123456", + "attachScreenshot": true + ]) + + let maskedViewClasses = SentrySDK.internal.options.screenshot.maskedViewClasses + XCTAssertTrue(maskedViewClasses.contains { $0 == RNSentryReplay.getMaskClass() }) + } + + func testScreenshotKeepsUserMaskedViewClassesWithSentryMaskComponent() throws { + try startFromRN(options: [ + "dsn": "https://abcd@efgh.ingest.sentry.io/123456", + "attachScreenshot": true, + "screenshot": [ + "maskedViewClasses": ["UILabel"] + ] + ]) + + let maskedViewClasses = SentrySDK.internal.options.screenshot.maskedViewClasses + XCTAssertTrue(maskedViewClasses.contains { $0 == UILabel.self }) + XCTAssertTrue(maskedViewClasses.contains { $0 == RNSentryReplay.getMaskClass() }) + } + func testMemoryIntrospectionOption() throws { try startFromRN(options: [ "dsn": "https://abcd@efgh.ingest.sentry.io/123456", diff --git a/packages/core/ios/RNSentryStart.m b/packages/core/ios/RNSentryStart.m index 3ff2ae837c..7f5113db9a 100644 --- a/packages/core/ios/RNSentryStart.m +++ b/packages/core/ios/RNSentryStart.m @@ -121,6 +121,18 @@ + (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull) sentryOptions.screenshot.unmaskedViewClasses = classes; } } + + // Honor in error screenshots, the same as in Session Replay. The native SDK only + // lets hybrid SDKs set the mask container class on the replay photographer, so register it as + // a masked view class instead: the wrapper's whole frame is masked, covering its children. + // needs a container class (children are unmasked), which isn't available yet. +# if SENTRY_TARGET_REPLAY_SUPPORTED + Class maskClass = [RNSentryReplay getMaskClass]; + if (![sentryOptions.screenshot.maskedViewClasses containsObject:maskClass]) { + sentryOptions.screenshot.maskedViewClasses = + [sentryOptions.screenshot.maskedViewClasses arrayByAddingObject:maskClass]; + } +# endif #endif // Exclude Dev Server and Sentry Dsn request from Breadcrumbs