Skip to content

Commit

Permalink
Rename session replay redact options and APIs to mask (#4373)
Browse files Browse the repository at this point in the history
Renaming SR redact to mask and ignoretounmask
  • Loading branch information
brustolin authored Oct 4, 2024
1 parent 4314fa2 commit 2f5e5f7
Show file tree
Hide file tree
Showing 17 changed files with 74 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/benchmarking.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ jobs:
name: raw-build-output-build-xcframework
path: |
build-xcframework.log
- name: Build test app with sentry
run: bundle exec fastlane build_perf_test_app_sentry
env:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

- Speed up HTTP tracking for multiple requests in parallel (#4366)
- Slightly speed up SentryInAppLogic (#4370)
- Rename session replay `redact` options and APIs to `mask` (#4373)
- Stop canceling timer for manual transactions (#4380)

### Fixes
Expand Down
4 changes: 2 additions & 2 deletions Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ - (BOOL)application:(UIApplication *)application
options.failedRequestStatusCodes = @[ httpStatusCodeRange ];

options.experimental.sessionReplay.quality = SentryReplayQualityMedium;
options.experimental.sessionReplay.redactAllText = true;
options.experimental.sessionReplay.redactAllImages = true;
options.experimental.sessionReplay.maskAllText = true;
options.experimental.sessionReplay.maskAllImages = true;
options.experimental.sessionReplay.sessionSampleRate = 0;
options.experimental.sessionReplay.onErrorSampleRate = 1;

Expand Down
2 changes: 1 addition & 1 deletion Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
options.debug = true

if #available(iOS 16.0, *), !args.contains("--disable-session-replay") {
options.experimental.sessionReplay = SentryReplayOptions(sessionSampleRate: 1, onErrorSampleRate: 1, redactAllText: true, redactAllImages: true)
options.experimental.sessionReplay = SentryReplayOptions(sessionSampleRate: 1, onErrorSampleRate: 1, maskAllText: true, maskAllImages: true)
options.experimental.sessionReplay.quality = .high
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ class SRRedactSampleViewController: UIViewController {
notRedactedView.backgroundColor = .green
notRedactedView.transform = CGAffineTransform(rotationAngle: 45 * .pi / 180.0)

SentrySDK.replay.ignoreView(notRedactedView)
SentrySDK.replay.maskView(notRedactedView)
}
}
2 changes: 1 addition & 1 deletion Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ struct ContentView: View {
Text("Form Screen")
}
}
.sentryReplayRedact()
.sentryReplayMask()
}
SecondView()
}
Expand Down
4 changes: 2 additions & 2 deletions Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ struct SwiftUIApp: App {
options.tracesSampleRate = 1.0
options.profilesSampleRate = 1.0
options.experimental.sessionReplay.sessionSampleRate = 1.0
options.experimental.sessionReplay.redactAllImages = false
options.experimental.sessionReplay.redactAllText = false
options.experimental.sessionReplay.maskAllImages = false
options.experimental.sessionReplay.maskAllText = false
options.initialScope = { scope in
scope.injectGitInformation()
return scope
Expand Down
9 changes: 4 additions & 5 deletions Sources/Sentry/Public/SentryReplayApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,18 @@ NS_ASSUME_NONNULL_BEGIN
@interface SentryReplayApi : NSObject

/**
* Marks this view to be redacted during replays.
* Marks this view to be masked during replays.
*
* @warning This is an experimental feature and may still have bugs.
*/
- (void)redactView:(UIView *)view NS_SWIFT_NAME(redactView(_:));
- (void)maskView:(UIView *)view NS_SWIFT_NAME(maskView(_:));

/**
* Marks this view to be ignored during redact step of session replay.
* All its content will be visible in the replay.
* Marks this view to not be masked during redact step of session replay.
*
* @warning This is an experimental feature and may still have bugs.
*/
- (void)ignoreView:(UIView *)view NS_SWIFT_NAME(ignoreView(_:));
- (void)unmaskView:(UIView *)view NS_SWIFT_NAME(unmaskView(_:));

/**
* Pauses the replay.
Expand Down
8 changes: 4 additions & 4 deletions Sources/Sentry/SentryReplayApi.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

@implementation SentryReplayApi

- (void)redactView:(UIView *)view
- (void)maskView:(UIView *)view
{
[SentryRedactViewHelper redactView:view];
[SentryRedactViewHelper maskView:view];
}

- (void)ignoreView:(UIView *)view
- (void)unmaskView:(UIView *)view
{
[SentryRedactViewHelper ignoreView:view];
[SentryRedactViewHelper unmaskView:view];
}

- (void)pause
Expand Down
8 changes: 4 additions & 4 deletions Sources/SentrySwiftUI/SentryReplayView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct SentryReplayView: UIViewRepresentable {

func makeUIView(context: Context) -> UIView {
let result = SentryRedactView()
result.sentryReplayRedact()
result.sentryReplayMask()
return result
}

Expand All @@ -29,15 +29,15 @@ struct SentryReplayModifier: ViewModifier {
@available(iOS 13, macOS 10.15, tvOS 13, *)
public extension View {

/// Marks the view as containing sensitive information that should be redacted during replays.
/// Marks the view as containing sensitive information that should be masked during replays.
///
/// When this modifier is applied, any sensitive content within the view will be hidden or masked
/// When this modifier is applied, any sensitive content within the view will be masked
/// during session replays to ensure user privacy. This is useful for views containing personal
/// data or confidential information that shouldn't be visible when the replay is reviewed.
///
/// - Returns: A modifier that redacts sensitive information during session replays.
/// - Experiment: This is an experimental feature and may still have bugs.
func sentryReplayRedact() -> some View {
func sentryReplayMask() -> some View {
modifier(SentryReplayModifier())
}
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/Swift/Extensions/UIViewExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ public extension UIView {
* Marks this view to be redacted during replays.
* - experiment: This is an experimental feature and may still have bugs.
*/
func sentryReplayRedact() {
SentryRedactViewHelper.redactView(self)
func sentryReplayMask() {
SentryRedactViewHelper.maskView(self)
}

/**
* Marks this view to be ignored during redact step
* of session replay. All its content will be visible in the replay.
* - experiment: This is an experimental feature and may still have bugs.
*/
func sentryReplayIgnore() {
SentryRedactViewHelper.ignoreView(self)
func sentryReplayUnmask() {
SentryRedactViewHelper.unmaskView(self)
}
}

Expand Down
20 changes: 10 additions & 10 deletions Sources/Swift/Integrations/SessionReplay/SentryReplayOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ public class SentryReplayOptions: NSObject, SentryRedactOptions {
*
* - note: The default is true
*/
public var redactAllText = true
public var maskAllText = true

/**
* Indicates whether session replay should redact all non-bundled image
* in the app by drawing a black rectangle over it.
*
* - note: The default is true
*/
public var redactAllImages = true
public var maskAllImages = true

/**
* Indicates the quality of the replay.
Expand All @@ -73,15 +73,15 @@ public class SentryReplayOptions: NSObject, SentryRedactOptions {
* By default Sentry already mask text and image elements from UIKit
* Every child of a view that is redacted will also be redacted.
*/
public var redactViewClasses = [AnyClass]()
public var maskedViewClasses = [AnyClass]()

/**
* A list of custom UIView subclasses to be ignored
* during masking step of the session replay.
* The views of given classes will not be redacted but their children may be.
* This property has precedence over `redactViewTypes`.
*/
public var ignoreViewClasses = [AnyClass]()
public var unmaskedViewClasses = [AnyClass]()

/**
* Defines the quality of the session replay.
Expand Down Expand Up @@ -139,18 +139,18 @@ public class SentryReplayOptions: NSObject, SentryRedactOptions {
* - errorSampleRate Indicates the percentage in which a 30 seconds replay will be send with
* error events.
*/
public init(sessionSampleRate: Float = 0, onErrorSampleRate: Float = 0, redactAllText: Bool = true, redactAllImages: Bool = true) {
public init(sessionSampleRate: Float = 0, onErrorSampleRate: Float = 0, maskAllText: Bool = true, maskAllImages: Bool = true) {
self.sessionSampleRate = sessionSampleRate
self.onErrorSampleRate = onErrorSampleRate
self.redactAllText = redactAllText
self.redactAllImages = redactAllImages
self.maskAllText = maskAllText
self.maskAllImages = maskAllImages
}

convenience init(dictionary: [String: Any]) {
let sessionSampleRate = (dictionary["sessionSampleRate"] as? NSNumber)?.floatValue ?? 0
let onErrorSampleRate = (dictionary["errorSampleRate"] as? NSNumber)?.floatValue ?? 0
let redactAllText = (dictionary["redactAllText"] as? NSNumber)?.boolValue ?? true
let redactAllImages = (dictionary["redactAllImages"] as? NSNumber)?.boolValue ?? true
self.init(sessionSampleRate: sessionSampleRate, onErrorSampleRate: onErrorSampleRate, redactAllText: redactAllText, redactAllImages: redactAllImages)
let maskAllText = (dictionary["maskAllText"] as? NSNumber)?.boolValue ?? true
let maskAllImages = (dictionary["maskAllImages"] as? NSNumber)?.boolValue ?? true
self.init(sessionSampleRate: sessionSampleRate, onErrorSampleRate: onErrorSampleRate, maskAllText: maskAllText, maskAllImages: maskAllImages)
}
}
8 changes: 4 additions & 4 deletions Sources/Swift/Protocol/SentryRedactOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Foundation

@objc
protocol SentryRedactOptions {
var redactAllText: Bool { get }
var redactAllImages: Bool { get }
var redactViewClasses: [AnyClass] { get }
var ignoreViewClasses: [AnyClass] { get }
var maskAllText: Bool { get }
var maskAllImages: Bool { get }
var maskedViewClasses: [AnyClass] { get }
var unmaskedViewClasses: [AnyClass] { get }
}
34 changes: 17 additions & 17 deletions Sources/Swift/Tools/UIRedactBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,21 @@ class UIRedactBuilder {
- parameter options: A `SentryRedactOptions` object that specifies the configuration for the redaction process.
- If `options.redactAllText` is `true`, common text-related views such as `UILabel`, `UITextView`, and `UITextField` are redacted.
- If `options.redactAllImages` is `true`, common image-related views such as `UIImageView` and various internal `SwiftUI` image views are redacted.
- The `options.ignoreRedactViewTypes` allows specifying custom view types to be ignored during the redaction process.
- The `options.redactViewTypes` allows specifying additional custom view types to be redacted.
- If `options.maskAllText` is `true`, common text-related views such as `UILabel`, `UITextView`, and `UITextField` are redacted.
- If `options.maskAllImages` is `true`, common image-related views such as `UIImageView` and various internal `SwiftUI` image views are redacted.
- The `options.unmaskViewTypes` allows specifying custom view types to be ignored during the redaction process.
- The `options.maskViewTypes` allows specifying additional custom view types to be redacted.
- note: On iOS, views such as `WKWebView` and `UIWebView` are automatically redacted, and controls like `UISlider` and `UISwitch` are ignored.
*/
init(options: SentryRedactOptions) {
var redactClasses = [AnyClass]()

if options.redactAllText {
if options.maskAllText {
redactClasses += [ UILabel.self, UITextView.self, UITextField.self ]
}

if options.redactAllImages {
if options.maskAllImages {
//this classes are used by SwiftUI to display images.
redactClasses += ["_TtCOCV7SwiftUI11DisplayList11ViewUpdater8Platform13CGDrawingView",
"_TtC7SwiftUIP33_A34643117F00277B93DEBAB70EC0697122_UIShapeHitTestingView",
Expand All @@ -89,11 +89,11 @@ class UIRedactBuilder {

redactClassesIdentifiers = Set(redactClasses.map({ ObjectIdentifier($0) }))

for type in options.ignoreViewClasses {
for type in options.unmaskedViewClasses {
self.ignoreClassesIdentifiers.insert(ObjectIdentifier(type))
}

for type in options.redactViewClasses {
for type in options.maskedViewClasses {
self.redactClassesIdentifiers.insert(ObjectIdentifier(type))
}
}
Expand Down Expand Up @@ -133,15 +133,15 @@ class UIRedactBuilder {
This function identifies and returns the regions within a given UIView that need to be redacted, based on the specified redaction options.
- Parameter view: The root UIView for which redaction regions are to be calculated.
- Parameter options: A `SentryRedactOptions` object specifying whether to redact all text (`redactAllText`) or all images (`redactAllImages`). If `options` is nil, defaults are used (redacting all text and images).
- Parameter options: A `SentryRedactOptions` object specifying whether to redact all text (`maskAllText`) or all images (`maskAllImages`). If `options` is nil, defaults are used (redacting all text and images).
- Returns: An array of `RedactRegion` objects representing areas of the view (and its subviews) that require redaction, based on the current visibility, opacity, and content (text or images).
The method recursively traverses the view hierarchy, collecting redaction areas from the view and all its subviews. Each redaction area is calculated based on the view’s presentation layer, size, transformation matrix, and other attributes.
The redaction process considers several key factors:
1. **Text Redaction**: If `redactAllText` is set to true, regions containing text within the view or its subviews are marked for redaction.
2. **Image Redaction**: If `redactAllImages` is set to true, image-containing regions are also marked for redaction.
1. **Text Redaction**: If `maskAllText` is set to true, regions containing text within the view or its subviews are marked for redaction.
2. **Image Redaction**: If `maskAllImages` is set to true, image-containing regions are also marked for redaction.
3. **Opaque View Handling**: If an opaque view covers the entire area, obfuscating views beneath it, those hidden views are excluded from processing, and we can remove them from the result.
4. **Clip Area Creation**: If a smaller opaque view blocks another view, we create a clip area to avoid drawing a redact mask on top of a view that does not require redaction.
Expand All @@ -159,11 +159,11 @@ class UIRedactBuilder {
}

private func shouldIgnore(view: UIView) -> Bool {
return SentryRedactViewHelper.shouldIgnoreView(view) || containsIgnoreClass(type(of: view))
return SentryRedactViewHelper.shouldUnmask(view) || containsIgnoreClass(type(of: view))
}

private func shouldRedact(view: UIView) -> Bool {
if SentryRedactViewHelper.shouldRedactView(view) {
if SentryRedactViewHelper.shouldMaskView(view) {
return true
}
if let imageView = view as? UIImageView, containsRedactClass(UIImageView.self) {
Expand Down Expand Up @@ -257,19 +257,19 @@ class SentryRedactViewHelper: NSObject {
private static var associatedRedactObjectHandle: UInt8 = 0
private static var associatedIgnoreObjectHandle: UInt8 = 0

static func shouldRedactView(_ view: UIView) -> Bool {
static func shouldMaskView(_ view: UIView) -> Bool {
(objc_getAssociatedObject(view, &associatedRedactObjectHandle) as? NSNumber)?.boolValue ?? false
}

static func shouldIgnoreView(_ view: UIView) -> Bool {
static func shouldUnmask(_ view: UIView) -> Bool {
(objc_getAssociatedObject(view, &associatedIgnoreObjectHandle) as? NSNumber)?.boolValue ?? false
}

static func redactView(_ view: UIView) {
static func maskView(_ view: UIView) {
objc_setAssociatedObject(view, &associatedRedactObjectHandle, true, .OBJC_ASSOCIATION_ASSIGN)
}

static func ignoreView(_ view: UIView) {
static func unmaskView(_ view: UIView) {
objc_setAssociatedObject(view, &associatedIgnoreObjectHandle, true, .OBJC_ASSOCIATION_ASSIGN)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ class SentrySessionReplayIntegrationTests: XCTestCase {
}

startSDK(sessionSampleRate: 1, errorSampleRate: 1) { options in
options.experimental.sessionReplay.redactViewClasses = [AnotherLabel.self]
options.experimental.sessionReplay.maskedViewClasses = [AnotherLabel.self]
}

let sut = try getSut()
Expand All @@ -301,7 +301,7 @@ class SentrySessionReplayIntegrationTests: XCTestCase {
}

startSDK(sessionSampleRate: 1, errorSampleRate: 1) { options in
options.experimental.sessionReplay.ignoreViewClasses = [AnotherLabel.self]
options.experimental.sessionReplay.unmaskedViewClasses = [AnotherLabel.self]
}

let sut = try getSut()
Expand Down
2 changes: 1 addition & 1 deletion Tests/SentryTests/SwiftUI/SentryRedactModifierTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class SentryRedactModifierTests: XCTestCase {

func testViewRedacted() throws {
let text = Text("Hello, World!")
let redactedText = text.sentryReplayRedact()
let redactedText = text.sentryReplayMask()

XCTAssertTrue(redactedText is ModifiedContent<Text, SentryReplayModifier>)
}
Expand Down
Loading

0 comments on commit 2f5e5f7

Please sign in to comment.