From d6ff82cd1e1d0d263fb1a7358b09bd0a08df013e Mon Sep 17 00:00:00 2001 From: Krystof Woldrich <31292499+krystofwoldrich@users.noreply.github.com> Date: Thu, 25 Apr 2024 12:19:02 +0200 Subject: [PATCH] feat(replay): Expose ignore and redact classes to hybrid SDKs (#3891) --- Sources/Sentry/PrivateSentrySDKOnly.mm | 31 +++++++++++++++++++ .../HybridPublic/PrivateSentrySDKOnly.h | 2 ++ .../Swift/Tools/SentryViewPhotographer.swift | 12 ++++++- .../PrivateSentrySDKOnlyTests.swift | 24 ++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Sources/Sentry/PrivateSentrySDKOnly.mm b/Sources/Sentry/PrivateSentrySDKOnly.mm index 125392ec7ee..52bc98e8ce2 100644 --- a/Sources/Sentry/PrivateSentrySDKOnly.mm +++ b/Sources/Sentry/PrivateSentrySDKOnly.mm @@ -272,4 +272,35 @@ + (NSString *__nullable)getReplayId return replayId; } ++ (void)addReplayIgnoreClasses:(NSArray *_Nonnull)classes +{ +#if SENTRY_HAS_UIKIT && !TARGET_OS_VISION + if (@available(iOS 16.0, tvOS 16.0, *)) { + [SentryViewPhotographer.shared addIgnoreClasses:classes]; + } else { + SENTRY_LOG_DEBUG(@"PrivateSentrySDKOnly.addIgnoreClasses only works with iOS 16 and newer"); + } +#else + SENTRY_LOG_DEBUG( + @"PrivateSentrySDKOnly.addReplayIgnoreClasses only works with UIKit enabled and target is " + @"not visionOS. Ensure you're using the right configuration of Sentry that links UIKit."); +#endif +} + ++ (void)addReplayRedactClasses:(NSArray *_Nonnull)classes +{ +#if SENTRY_HAS_UIKIT && !TARGET_OS_VISION + if (@available(iOS 16.0, tvOS 16.0, *)) { + [SentryViewPhotographer.shared addRedactClasses:classes]; + } else { + SENTRY_LOG_DEBUG( + @"PrivateSentrySDKOnly.addReplayRedactClasses only works with iOS 16 and newer"); + } +#else + SENTRY_LOG_DEBUG( + @"PrivateSentrySDKOnly.addReplayRedactClasses only works with UIKit enabled and target is " + @"not visionOS. Ensure you're using the right configuration of Sentry that links UIKit."); +#endif +} + @end diff --git a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h index 06030dcbbb4..bf0c67e1040 100644 --- a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h +++ b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h @@ -164,6 +164,8 @@ typedef void (^SentryOnAppStartMeasurementAvailable)( + (void)captureReplay; + (NSString *__nullable)getReplayId; ++ (void)addReplayIgnoreClasses:(NSArray *_Nonnull)classes; ++ (void)addReplayRedactClasses:(NSArray *_Nonnull)classes; @end diff --git a/Sources/Swift/Tools/SentryViewPhotographer.swift b/Sources/Swift/Tools/SentryViewPhotographer.swift index 36667e4dec2..3ae3f54d7de 100644 --- a/Sources/Swift/Tools/SentryViewPhotographer.swift +++ b/Sources/Swift/Tools/SentryViewPhotographer.swift @@ -45,7 +45,17 @@ class SentryViewPhotographer: NSObject { guard let screenshot = UIGraphicsGetImageFromCurrentImageContext() else { return nil } return screenshot } - + + @objc(addIgnoreClasses:) + func addIgnoreClasses(classes: [AnyClass]) { + ignoreClasses += classes + } + + @objc(addRedactClasses:) + func addRedactClasses(classes: [AnyClass]) { + redactClasses += classes + } + private func mask(view: UIView, context: CGContext, options: SentryRedactOptions?) { UIColor.black.setFill() let maskPath = self.buildPath(view: view, diff --git a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift index ad0742c67d7..11c06271b8f 100644 --- a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift +++ b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift @@ -209,6 +209,14 @@ class PrivateSentrySDKOnlyTests: XCTestCase { PrivateSentrySDKOnly.captureReplay() } + func testAddReplayIgnoreClassesShouldNotFailIfMissingReplayIntegration() { + PrivateSentrySDKOnly.addReplayIgnoreClasses([]) + } + + func testAddReplayRedactShouldNotFailIfMissingReplayIntegration() { + PrivateSentrySDKOnly.addReplayRedactClasses([]) + } + #if canImport(UIKit) func testCaptureReplayShouldCallReplayIntegration() { guard #available(iOS 16.0, tvOS 16.0, *) else { return } @@ -235,6 +243,22 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertEqual(PrivateSentrySDKOnly.getReplayId(), VALID_REPLAY_ID) } + func testAddReplayIgnoreClassesShouldNotFailWhenReplayIsAvailable() { + let options = Options() + options.setIntegrations([SentrySessionReplayIntegrationTest.self]) + SentrySDK.start(options: options) + + PrivateSentrySDKOnly.addReplayIgnoreClasses([UILabel.self]) + } + + func testAddReplayRedactShouldNotFailWhenReplayIsAvailable() { + let options = Options() + options.setIntegrations([SentrySessionReplayIntegrationTest.self]) + SentrySDK.start(options: options) + + PrivateSentrySDKOnly.addReplayRedactClasses([UILabel.self]) + } + let VALID_REPLAY_ID = "0eac7ab503354dd5819b03e263627a29" final class SentrySessionReplayIntegrationTest: SentrySessionReplayIntegration {