diff --git a/CHANGELOG.md b/CHANGELOG.md index 2892c5c91f8..e3714452079 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add start time to network request breadcrumbs (#4008) - Add C++ exception support for `__cxa_rethrow` (#3996) +- Add beforeCaptureScreenshot callback (#4016) ### Improvements diff --git a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift index af742089048..c5566ed501e 100644 --- a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift +++ b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift @@ -22,6 +22,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { options.beforeSend = { event in return event } + options.beforeCaptureScreenshot = { _ in + return true + } options.debug = true if #available(iOS 16.0, *) { diff --git a/Sources/Sentry/Public/SentryDefines.h b/Sources/Sentry/Public/SentryDefines.h index 7520e37c40d..6ea07e2db7f 100644 --- a/Sources/Sentry/Public/SentryDefines.h +++ b/Sources/Sentry/Public/SentryDefines.h @@ -81,6 +81,12 @@ typedef SentryBreadcrumb *_Nullable (^SentryBeforeBreadcrumbCallback)( */ typedef SentryEvent *_Nullable (^SentryBeforeSendEventCallback)(SentryEvent *_Nonnull event); +/** + * Block can be used to decide if the SDK should capture a screenshot or not. Return @c true if the + * SDK should capture a screenshot, return @c false if not. This callback doesn't work for crashes. + */ +typedef BOOL (^SentryBeforeCaptureScreenshotCallback)(SentryEvent *_Nonnull event); + /** * A callback to be notified when the last program execution terminated with a crash. */ diff --git a/Sources/Sentry/Public/SentryOptions.h b/Sources/Sentry/Public/SentryOptions.h index c1019db4d01..a7af2a0e2c4 100644 --- a/Sources/Sentry/Public/SentryOptions.h +++ b/Sources/Sentry/Public/SentryOptions.h @@ -105,6 +105,13 @@ NS_SWIFT_NAME(Options) */ @property (nullable, nonatomic, copy) SentryBeforeBreadcrumbCallback beforeBreadcrumb; +/** + * You can use this callback to decide if the SDK should capture a screenshot or not. Return @c true + * if the SDK should capture a screenshot, return @c false if not. This callback doesn't work for + * crashes. + */ +@property (nullable, nonatomic, copy) SentryBeforeCaptureScreenshotCallback beforeCaptureScreenshot; + /** * A block called shortly after the initialization of the SDK when the last program execution * terminated with a crash. diff --git a/Sources/Sentry/SentryOptions.m b/Sources/Sentry/SentryOptions.m index b74ce1620da..fceb8cbbc0f 100644 --- a/Sources/Sentry/SentryOptions.m +++ b/Sources/Sentry/SentryOptions.m @@ -339,6 +339,10 @@ - (BOOL)validateOptions:(NSDictionary *)options self.beforeBreadcrumb = options[@"beforeBreadcrumb"]; } + if ([self isBlock:options[@"beforeCaptureScreenshot"]]) { + self.beforeCaptureScreenshot = options[@"beforeCaptureScreenshot"]; + } + if ([self isBlock:options[@"onCrashedLastRun"]]) { self.onCrashedLastRun = options[@"onCrashedLastRun"]; } diff --git a/Sources/Sentry/SentryScreenshotIntegration.m b/Sources/Sentry/SentryScreenshotIntegration.m index e20b7ac7d43..8eda39c7a8c 100644 --- a/Sources/Sentry/SentryScreenshotIntegration.m +++ b/Sources/Sentry/SentryScreenshotIntegration.m @@ -8,6 +8,7 @@ # import "SentryEvent+Private.h" # import "SentryException.h" # import "SentryHub+Private.h" +# import "SentryOptions.h" # import "SentrySDK+Private.h" # if SENTRY_HAS_METRIC_KIT @@ -21,10 +22,19 @@ [SentryDependencyContainer.sharedInstance.screenshot saveScreenShots:reportPath]; } +@interface +SentryScreenshotIntegration () + +@property (nonatomic, strong) SentryOptions *options; + +@end + @implementation SentryScreenshotIntegration - (BOOL)installWithOptions:(nonnull SentryOptions *)options { + self.options = options; + if (![super installWithOptions:options]) { return NO; } @@ -70,6 +80,10 @@ - (void)uninstall return attachments; } + if (self.options.beforeCaptureScreenshot && !self.options.beforeCaptureScreenshot(event)) { + return attachments; + } + NSArray *screenshot = [SentryDependencyContainer.sharedInstance.screenshot appScreenshotsFromMainThread]; diff --git a/Tests/SentryTests/Integrations/Screenshot/SentryScreenshotIntegrationTests.swift b/Tests/SentryTests/Integrations/Screenshot/SentryScreenshotIntegrationTests.swift index 0857d3b48ee..f39be6b71df 100644 --- a/Tests/SentryTests/Integrations/Screenshot/SentryScreenshotIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/Screenshot/SentryScreenshotIntegrationTests.swift @@ -137,6 +137,26 @@ class SentryScreenshotIntegrationTests: XCTestCase { } #endif // os(iOS) || targetEnvironment(macCatalyst) + func test_NoScreenShot_WhenDiscardedInCallback() { + let sut = fixture.getSut() + + let expectation = expectation(description: "BeforeCaptureScreenshot must be called.") + + let options = Options() + options.beforeCaptureScreenshot = { _ in + expectation.fulfill() + return false + } + + sut.install(with: options) + + let newAttachmentList = sut.processAttachments([], for: Event(error: NSError(domain: "", code: -1))) + + wait(for: [expectation], timeout: 1.0) + + XCTAssertEqual(newAttachmentList?.count, 0) + } + func test_noScreenshot_keepAttachment() { let sut = fixture.getSut() let event = Event() diff --git a/Tests/SentryTests/SentryOptionsTest.m b/Tests/SentryTests/SentryOptionsTest.m index d50996eb8dc..276ba0420e4 100644 --- a/Tests/SentryTests/SentryOptionsTest.m +++ b/Tests/SentryTests/SentryOptionsTest.m @@ -310,6 +310,27 @@ - (void)testDefaultBeforeBreadcrumb XCTAssertNil(options.beforeBreadcrumb); } +- (void)testBeforeCaptureScreenshot +{ + SentryBeforeCaptureScreenshotCallback callback = ^(SentryEvent *event) { + if (event.level == kSentryLevelFatal) { + return NO; + } + + return YES; + }; + SentryOptions *options = [self getValidOptions:@{ @"beforeCaptureScreenshot" : callback }]; + + XCTAssertEqual(callback, options.beforeCaptureScreenshot); +} + +- (void)testDefaultBeforeCaptureScreenshot +{ + SentryOptions *options = [self getValidOptions:@{}]; + + XCTAssertNil(options.beforeCaptureScreenshot); +} + - (void)testTracePropagationTargets { SentryOptions *options =