Skip to content

Conversation

@itaybre
Copy link
Contributor

@itaybre itaybre commented Nov 2, 2025

📜 Description

Improve check for view opaqueness

💡 Motivation and Context

User's (or libraries) can override UIView's backgroundColor and report a report different value (example).
Since UIView are wrappers around CALayer. we should consider using the actual layers properties too.

This is draft because I have to verify this actually works

💚 How did you test it?

Run the app with Session Replay enabled and view the output.
Still needs more tests with popups and custom views

📝 Checklist

You have to check all boxes before merging:

  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

Closes #6634

philprime and others added 15 commits October 28, 2025 16:13
Added expectations to verify the animation state during redaction, ensuring the position of the masked region is within the expected range during animation. This improves the robustness of the edge case tests for UI redaction.
…se tests

Updated the animation duration and expectations in the edge case tests for UI redaction. The assertions now verify that the position of the masked region is approximately at the midpoint of the animation, enhancing the accuracy of the tests.
@codecov
Copy link

codecov bot commented Nov 2, 2025

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
3933 1 3932 55
View the top 1 failed test(s) by shortest run time
SentryTests.SentryUIRedactBuilderTests_Common::testDefaultIgnoredControls_shouldNotRedactUISlider
Stack Traces | 0s run time
.../SentryTests/ViewCapture/SentryUIRedactBuilderTests+Common.swift:1128 - XCTAssertEqual failed: ("2") is not equal to ("0")

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 2, 2025

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1212.35 ms 1245.35 ms 32.99 ms
Size 23.75 KiB 1023.93 KiB 1000.19 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
7f4bf81 1241.73 ms 1270.66 ms 28.93 ms
afaa522 1234.71 ms 1256.19 ms 21.48 ms
0b6776b 1230.18 ms 1262.06 ms 31.88 ms
c6c1cb7 1235.71 ms 1263.80 ms 28.08 ms
4e3915a 1230.02 ms 1258.90 ms 28.88 ms
d72784d 1214.31 ms 1241.35 ms 27.04 ms
37238de 1236.00 ms 1267.80 ms 31.80 ms
dad68ad 1229.15 ms 1261.98 ms 32.83 ms
5ae9ff1 1222.31 ms 1250.96 ms 28.65 ms
b115f82 1212.96 ms 1251.92 ms 38.96 ms

App size

Revision Plain With Sentry Diff
7f4bf81 23.75 KiB 919.70 KiB 895.95 KiB
afaa522 23.74 KiB 996.91 KiB 973.17 KiB
0b6776b 23.75 KiB 968.23 KiB 944.49 KiB
c6c1cb7 23.75 KiB 928.15 KiB 904.40 KiB
4e3915a 23.75 KiB 858.69 KiB 834.94 KiB
d72784d 23.75 KiB 988.01 KiB 964.27 KiB
37238de 23.75 KiB 963.18 KiB 939.43 KiB
dad68ad 23.75 KiB 912.37 KiB 888.63 KiB
5ae9ff1 23.74 KiB 971.82 KiB 948.08 KiB
b115f82 23.75 KiB 989.04 KiB 965.30 KiB

Previous results on branch: itay/improve_opaque_logic

Startup times

Revision Plain With Sentry Diff
8858a16 1225.98 ms 1255.96 ms 29.98 ms
27fdaa1 1232.08 ms 1265.20 ms 33.11 ms
65af6ea 1219.83 ms 1250.20 ms 30.37 ms

App size

Revision Plain With Sentry Diff
8858a16 23.75 KiB 1.01 MiB 1016.01 KiB
27fdaa1 23.75 KiB 1.02 MiB 1019.46 KiB
65af6ea 23.75 KiB 1.02 MiB 1016.57 KiB

@philprime philprime changed the title fix: Include layer background color when checkig if a view is opaque fix(session-replay): Include layer background color when checkig if a view is opaque Nov 3, 2025
@philprime
Copy link
Member

philprime commented Nov 3, 2025

I followed up the investigation and confirm the issue and the fix:

  1. To cause the original issue, add the Orderella/PopupDialog as a dependency
  2. Then create a new popup like this in any view controller, e.g in the viewDidAppear(_:):
let popup = PopupDialog(
    title: "THIS IS THE DIALOG TITLE",
    message: "This is the message section of the popup dialog default view",
)

let overlayAppearance = PopupDialogOverlayView.appearance()
overlayAppearance.color           = .red
overlayAppearance.blurEnabled     = false
overlayAppearance.liveBlurEnabled = false
overlayAppearance.opacity         = 0.2

self.present(popup, animated: true) {
    print(self.view.window!.value(forKey: "recursiveDescription")!)
}
  1. It will overlay the view controller with a faint red tint:
783854750 454676
(lldb) po self.view.window!.value(forKey: "recursiveDescription")!
<UIWindow: 0x10371f540; frame = (0 0; 402 874); gestureRecognizers = <NSArray: 0x600000c74210>; backgroundColor = <UIDynamicSystemColor: 0x600001761040; name = _windowBackgroundColor>; layer = <UIWindowLayer: 0x600001765300>>
   | <UITransitionView: 0x10374b010; frame = (0 0; 402 874); autoresize = W+H; layer = <CALayer: 0x600000c8e4c0>>
   |    | <UIDropShadowView: 0x10374b500; frame = (0 0; 402 874); autoresize = W+H; layer = <CALayer: 0x600000c8db30>>
   |    |    | <UILayoutContainerView: 0x10320c560; frame = (0 0; 402 874); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000022f00>; layer = <CALayer: 0x600000c86940>>
   |    |    |    | <UINavigationTransitionView: 0x1037490b0; frame = (0 0; 402 874); autoresize = W+H; layer = <CALayer: 0x600000c8cf30>>
   |    |    |    |    | <UIViewControllerWrapperView: 0x103741ec0; frame = (0 0; 402 874); autoresize = W+H; layer = <CALayer: 0x600000c6edf0>>
   |    |    |    |    |    | <UILayoutContainerView: 0x103724320; frame = (0 0; 402 874); autoresize = W+H; backgroundColor = <UIDynamicSystemColor: 0x60000176c700; name = systemBackgroundColor>; layer = <CALayer: 0x600000c6ed60>>
   |    |    |    |    |    |    | <UITransitionView: 0x1037076d0; frame = (0 0; 402 874); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x600000c6e9a0>>
   |    |    |    |    |    |    |    | <UIViewControllerWrapperView: 0x1032a62b0; frame = (0 0; 402 874); autoresize = W+H; layer = <CALayer: 0x600000c96430>>
   |    |    |    |    |    |    |    |    | <UIView: 0x1032946e0; frame = (0 0; 402 874); autoresize = W+H; backgroundColor = <UIDynamicSystemColor: 0x60000176c700; name = systemBackgroundColor>; layer = <CALayer: 0x600000c96760>>
   |    |    |    |    |    |    |    |    |    | <UIStackView: 0x103298000; frame = (0 100.333; 402 274.333); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x600000c964f0>> axis=vert distribution=fill alignment=fill
   |    |    |    |    |    |    |    |    |    |    | <UIStackView: 0x10329c690; frame = (0 0; 402 146.333); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x600000c96730>> axis=horiz distribution=fillEqually alignment=fill
   |    |    |    |    |    |    |    |    |    |    |    | <UIStackView: 0x1032a2a80; frame = (0 0; 201 146.333); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x600000c96220>> axis=vert distribution=equalSpacing alignment=fill
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x1032a2c40; frame = (0 0; 201 28); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002cbf40>; layer = <CALayer: 0x600000c95ce0>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1032a9730; frame = (59.6667 6; 82 16); text = 'Capture Error'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c5b680>>
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x1032a3190; frame = (0 29.6667; 201 28); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002cd420>; layer = <CALayer: 0x600000c969d0>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1032a93f0; frame = (35.6667 6; 130 16); text = 'Capture NSException'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c5b580>>
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x10371b640; frame = (0 59.3333; 201 28); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002b9740>; layer = <CALayer: 0x600000c6e550>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1032a90b0; frame = (50 6; 101 16); text = 'Throw FatalError'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c5ac80>>
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x1032a3480; frame = (0 88.6667; 201 28); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002a4f80>; layer = <CALayer: 0x600000c96c10>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1032a8c40; frame = (25.6667 6; 150 16); text = 'Fatal Duplicate Key Error'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c5b400>>
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x103613d20; frame = (0 118.333; 201 28); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002cece0>; layer = <CALayer: 0x600000c82100>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1032a6e50; frame = (66 6; 69 16); text = 'OOM crash'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c5ab80>>
   |    |    |    |    |    |    |    |    |    |    |    | <UIStackView: 0x10371adc0; frame = (201 0; 201 146.333); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x600000c6cbd0>> axis=vert distribution=equalSpacing alignment=fill
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x103739ef0; frame = (0 0; 201 28); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002b9920>; layer = <CALayer: 0x600000c6cc60>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1032a7d10; frame = (33 6; 135 16); text = 'Force unwrap optional'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c5b000>>
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x1032a3770; frame = (0 28; 201 28); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002a5b00>; layer = <CALayer: 0x600000c96d30>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1032a79d0; frame = (41.6667 6; 118 16); text = 'DiskWriteException'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c5af00>>
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x10361b970; frame = (0 56; 201 28); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002cfa60>; layer = <CALayer: 0x600000c82310>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1032a7690; frame = (57.6667 6; 86 16); text = 'Crash the app'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c5ae00>>
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x1037079e0; frame = (0 84; 201 28); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002bba80>; layer = <CALayer: 0x600000c6d380>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1032a7190; frame = (21.6667 6; 158 16); text = 'Unhandled C++ Exception'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c5ad00>>
   |    |    |    |    |    |    |    |    |    |    |    |    | <UIButton: 0x1032a3a60; frame = (0 112; 201 34.3333); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000002cbde0>; layer = <CALayer: 0x600000c96f70>> configuration=<UIButtonConfiguration: 0x1032a5480> baseStyle=plain macStyle=automatic buttonSize=medium cornerStyle=dynamic title=<0x6000002b4020>:'Use-after-free' contentInsets=default imagePlacement=leading imagePadding=0 titlePadding=1 titleAlignment=automatic automaticallyUpdateForSelection background=<UIBackgroundConfiguration: 0x600003b45340; Base Style = Custom; cornerRadius = 5.95>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <_UISystemBackgroundView: 0x1032a4e90; frame = (0 0; 201 34.3333); userInteractionEnabled = NO; layer = <CALayer: 0x600000c97180>; configuration = <UIBackgroundConfiguration: 0x600003b45420; Base Style = Custom; cornerRadius = 6>>
   |    |    |    |    |    |    |    |    |    |    |    |    |    | <UILabel: 0x1032a4760; frame = (45.3333 7; 110 20.3333); text = 'Use-after-free'; userInteractionEnabled = NO; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <_UILabelLayer: 0x600002c59f80>>
   |    |    |    |    |    |    |    |    |    |    | <UIImageView: 0x10320e350; frame = (0 146.333; 402 128); clipsToBounds = YES; autoresize = RM+BM; userInteractionEnabled = NO; image = <(null):0x0 (null) anonymous; (0 0)@0>; layer = <CALayer: 0x600000c95cb0>>
   |    |    |    |    |    |    | <UITabBar: 0x1038067e0; frame = (0 791; 402 83); autoresize = W+TM; gestureRecognizers = <NSArray: 0x60000000a520>; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <CALayer: 0x600000c19a40>>
   |    |    |    |    |    |    |    | <_UIBarBackground: 0x1037387c0; frame = (0 0; 402 83); userInteractionEnabled = NO; layer = <CALayer: 0x600000c77600>>
   |    |    |    |    |    |    |    |    | <UIVisualEffectView: 0x10362d330; frame = (0 0; 402 83); alpha = 0; layer = <CALayer: 0x600000c832a0>> effect=none
   |    |    |    |    |    |    |    |    |    | <_UIVisualEffectBackdropView: 0x10362db60; frame = (0 0; 402 83); autoresize = W+H; userInteractionEnabled = NO; layer = <UICABackdropLayer: 0x600001786400>>
   |    |    |    |    |    |    |    |    | <_UIBarBackgroundShadowView: 0x10362d550; frame = (0 -0.333333; 402 0.333333); layer = <CALayer: 0x600000c83390>> clientRequestedContentView effect=none
   |    |    |    |    |    |    |    |    |    | <_UIBarBackgroundShadowContentImageView: 0x10362df30; frame = (0 0; 402 0.333333); alpha = 0; autoresize = W+H; userInteractionEnabled = NO; backgroundColor = <UIDynamicSystemColor: 0x6000017a0100; name = _systemChromeShadowColor>; image = <(null):0x0 (null) anonymous; (0 0)@0>; layer = <CALayer: 0x600000c83930>>
   |    |    |    |    |    |    |    | <UITabBarButton: 0x103605010; frame = (2 1; 76 48); opaque = NO; layer = <CALayer: 0x600000c0e940>>
   |    |    |    |    |    |    |    |    | <UIVisualEffectView: 0x10332a070; frame = (0 0; 76 48); userInteractionEnabled = NO; layer = <CALayer: 0x600000c79680>> clientRequestedContentView effect=<UIVibrancyEffect: 0x600000011030> style=UIBlurEffectStyleSystemChromeMaterial vibrancyStyle=UIVibrancyEffectStyleFill
   |    |    |    |    |    |    |    |    |    | <_UIVisualEffectContentView: 0x10332a3c0; frame = (0 0; 76 48); autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x600000c79830>> disablesGroupFiltering
   |    |    |    |    |    |    |    |    | <UITabBarSwappableImageView: 0x103617540; frame = (23.3333 4.33333; 28.6667 26); opaque = NO; userInteractionEnabled = NO; image = <UIImage:0x60000300d050 symbol "exclamationmark.triangle.fill"; (29 26)@3{2}>; layer = <CALayer: 0x600000c0ee80>>
   |    |    |    |    |    |    |    |    | <UITabBarButtonLabel: 0x103612050; frame = (22.6667 34.3333; 30.3333 12); text = 'Errors'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c0f880>>
   |    |    |    |    |    |    |    | <UITabBarButton: 0x10371eee0; frame = (82 1; 77 48); opaque = NO; layer = <CALayer: 0x600000c4c240>>
   |    |    |    |    |    |    |    |    | <UIVisualEffectView: 0x1033289e0; frame = (0 0; 77 48); userInteractionEnabled = NO; layer = <CALayer: 0x600000c79110>> clientRequestedContentView effect=<UIVibrancyEffect: 0x600000011030> style=UIBlurEffectStyleSystemChromeMaterial vibrancyStyle=UIVibrancyEffectStyleFill
   |    |    |    |    |    |    |    |    |    | <_UIVisualEffectContentView: 0x103328d30; frame = (0 0; 77 48); autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x600000c792c0>> disablesGroupFiltering
   |    |    |    |    |    |    |    |    |    |    | <UITabBarSwappableImageView: 0x10371f8d0; frame = (24.6667 4; 27.3333 27.3333); opaque = NO; userInteractionEnabled = NO; image = <UIImage:0x60000300eb50 symbol "clock.fill"; (27 27)@3{2}>; layer = <CALayer: 0x600000c4c450>>
   |    |    |    |    |    |    |    |    |    |    | <UITabBarButtonLabel: 0x10371f1d0; frame = (6.66667 34.3333; 63.6667 12); text = 'Transactions'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c38880>>
   |    |    |    |    |    |    |    | <UITabBarButton: 0x103724960; frame = (163 1; 76 48); opaque = NO; layer = <CALayer: 0x600000c03a80>>
   |    |    |    |    |    |    |    |    | <UIVisualEffectView: 0x103327630; frame = (0 0; 76 48); userInteractionEnabled = NO; layer = <CALayer: 0x600000c78ba0>> clientRequestedContentView effect=<UIVibrancyEffect: 0x600000011030> style=UIBlurEffectStyleSystemChromeMaterial vibrancyStyle=UIVibrancyEffectStyleFill
   |    |    |    |    |    |    |    |    |    | <_UIVisualEffectContentView: 0x103327980; frame = (0 0; 76 48); autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x600000c78d50>> disablesGroupFiltering
   |    |    |    |    |    |    |    |    |    |    | <UITabBarSwappableImageView: 0x103725220; frame = (24 4; 27.3333 27.3333); opaque = NO; userInteractionEnabled = NO; image = <UIImage:0x60000300f8d0 symbol "ellipsis.circle.fill"; (27 27)@3{2}>; layer = <CALayer: 0x600000c03810>>
   |    |    |    |    |    |    |    |    |    |    | <UITabBarButtonLabel: 0x103724c50; frame = (25 34.3333; 25.6667 12); text = 'Extra'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c39800>>
   |    |    |    |    |    |    |    | <UITabBarButton: 0x103726e20; frame = (243 1; 77 48); opaque = NO; layer = <CALayer: 0x600000c72850>>
   |    |    |    |    |    |    |    |    | <UIVisualEffectView: 0x103326280; frame = (0 0; 77 48); userInteractionEnabled = NO; layer = <CALayer: 0x600000c78630>> clientRequestedContentView effect=<UIVibrancyEffect: 0x600000011030> style=UIBlurEffectStyleSystemChromeMaterial vibrancyStyle=UIVibrancyEffectStyleFill
   |    |    |    |    |    |    |    |    |    | <_UIVisualEffectContentView: 0x1033265d0; frame = (0 0; 77 48); autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x600000c787e0>> disablesGroupFiltering
   |    |    |    |    |    |    |    |    |    |    | <UITabBarSwappableImageView: 0x10372eb90; frame = (26.6667 2.33333; 23.3333 28.6667); opaque = NO; userInteractionEnabled = NO; image = <UIImage:0x60000302a760 symbol "flame.fill"; (23 29)@3{2}>; layer = <CALayer: 0x600000c72880>>
   |    |    |    |    |    |    |    |    |    |    | <UITabBarButtonLabel: 0x10372e440; frame = (17.6667 34.3333; 41.6667 12); text = 'Profiling'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c39200>>
   |    |    |    |    |    |    |    | <UITabBarButton: 0x103730060; frame = (324 1; 76 48); opaque = NO; layer = <CALayer: 0x600000c74660>>
   |    |    |    |    |    |    |    |    | <UIVisualEffectView: 0x103630330; frame = (0 0; 76 48); userInteractionEnabled = NO; layer = <CALayer: 0x600000c83cc0>> clientRequestedContentView effect=<UIVibrancyEffect: 0x600000011030> style=UIBlurEffectStyleSystemChromeMaterial vibrancyStyle=UIVibrancyEffectStyleFill
   |    |    |    |    |    |    |    |    |    | <_UIVisualEffectContentView: 0x103318fe0; frame = (0 0; 76 48); autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x600000c78270>> disablesGroupFiltering
   |    |    |    |    |    |    |    |    |    |    | <UITabBarSwappableImageView: 0x103731d90; frame = (24 3.66667; 27.3333 27.6667); opaque = NO; userInteractionEnabled = NO; image = <UIImage:0x60000302b4e0 symbol "deskclock"; (27 28)@3{2}>; layer = <CALayer: 0x600000c74690>>
   |    |    |    |    |    |    |    |    |    |    | <UITabBarButtonLabel: 0x103731430; frame = (2.33333 34.3333; 71 12); text = 'Benchmarking'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c3bc80>>
   |    |    |    | <UINavigationBar: 0x10330b030; frame = (0 56.3333; 402 44); opaque = NO; autoresize = W; layer = <CALayer: 0x600000c52e20>> delegate=0x104834800
   |    |    |    |    | <_UIBarBackground: 0x10330b5f0; frame = (0 -62; 402 106); userInteractionEnabled = NO; layer = <CALayer: 0x600000c52a30>>
   |    |    |    |    |    | <UIVisualEffectView: 0x10352bd50; frame = (0 0; 402 106); alpha = 0; layer = <CALayer: 0x600000c90300>> effect=none
   |    |    |    |    |    |    | <_UIVisualEffectBackdropView: 0x10352b160; frame = (0 0; 402 106); autoresize = W+H; userInteractionEnabled = NO; layer = <UICABackdropLayer: 0x6000017a1140>>
   |    |    |    |    |    | <_UIBarBackgroundShadowView: 0x10352bf70; frame = (0 106; 402 0.333333); layer = <CALayer: 0x600000c903c0>> clientRequestedContentView effect=none
   |    |    |    |    |    |    | <_UIBarBackgroundShadowContentImageView: 0x10352d580; frame = (0 0; 402 0.333333); alpha = 0; autoresize = W+H; userInteractionEnabled = NO; backgroundColor = <UIDynamicSystemColor: 0x6000017a0100; name = _systemChromeShadowColor>; image = <(null):0x0 (null) anonymous; (0 0)@0>; layer = <CALayer: 0x600000c90c90>>
   |    |    |    |    | <_UINavigationBarContentView: 0x10330ba00; frame = (0 0; 402 44); layer = <CALayer: 0x600000c529d0>> layout=0x10330bf30
   |    |    |    |    |    | <_UIButtonBarStackView: 0x103529ca0; frame = (386 0; 8 44); layer = <CALayer: 0x600000c6fd20>> axis=horiz distribution=fill alignment=center layoutMarginsRelative NO ARRANGED SUBVIEWS buttonBar=0x600003e41e00
   |    |    |    |    | <_UIPointerInteractionAssistantEffectContainerView: 0x103528e20; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0x600000c6fae0>>
   | <UITransitionView: 0x103530530; frame = (0 0; 402 874); autoresize = W+H; layer = <CALayer: 0x600000cb8750>>
   |    | <PopupDialog.PopupDialogOverlayView: 0x10362e540; frame = (0 0; 402 874); autoresize = W+H; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <CALayer: 0x600000cb4840>>
   |    |    | <DynamicBlurView.DynamicBlurView: 0x10362f7b0; frame = (0 0; 402 874); hidden = YES; autoresize = W+H; userInteractionEnabled = NO; tintColor = UIExtendedGrayColorSpace 0 0; layer = <DynamicBlurView.BlurLayer: 0x600001772e80>>
   |    |    | <UIView: 0x103607260; frame = (0 0; 402 874); alpha = 0.2; autoresize = W+H; backgroundColor = UIExtendedSRGBColorSpace 1 0 0 1; layer = <CALayer: 0x600000cb4960>>
   |    | <PopupDialog.PopupDialogContainerView: 0x10362e370; frame = (0 0; 402 874); gestureRecognizers = <NSArray: 0x60000000e650>; backgroundColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x600000cb41e0>>
   |    |    | <UIView: 0x10362e740; frame = (31 377.667; 340 118.667); backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <CALayer: 0x600000cb4210>>
   |    |    |    | <UIView: 0x10362ea10; frame = (0 0; 340 118.667); clipsToBounds = YES; backgroundColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x600000cb4270>>
   |    |    |    |    | <UIStackView: 0x10362eea0; frame = (0 0; 340 118.667); gestureRecognizers = <NSArray: 0x60000000e6b0>; layer = <CALayer: 0x600000cb4420>> axis=vert distribution=fill alignment=fill
   |    |    |    |    |    | <UIStackView: 0x10362ece0; frame = (0 0; 0 0); layer = <CALayer: 0x600000cb4300>> axis=vert distribution=fillEqually alignment=fill NO ARRANGED SUBVIEWS
   |    |    |    |    |    | <PopupDialog.PopupDialogDefaultView: 0x1046053a0; frame = (0 0; 340 118.667); layer = <CALayer: 0x600000c228b0>>
   |    |    |    |    |    |    | <UIImageView: 0x1046058c0; frame = (0 0; 340 0); clipsToBounds = YES; userInteractionEnabled = NO; image = <(null):0x0 (null) anonymous; (0 0)@0>; layer = <CALayer: 0x600000c229d0>>
   |    |    |    |    |    |    | <UILabel: 0x104605cc0; frame = (20 30; 300 17); text = 'THIS IS THE DIALOG TITLE'; userInteractionEnabled = NO; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <_UILabelLayer: 0x600002c1cd00>>
   |    |    |    |    |    |    | <UILabel: 0x104606fd0; frame = (20 55; 300 33.6667); text = 'This is the message secti...'; userInteractionEnabled = NO; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <_UILabelLayer: 0x600002c1ce80>>

@philprime philprime changed the title fix(session-replay): Include layer background color when checkig if a view is opaque fix(session-replay): Include layer background color when checking if a view is opaque Nov 3, 2025
@philprime philprime self-assigned this Nov 3, 2025
@philprime philprime marked this pull request as ready for review November 3, 2025 12:58
@philprime
Copy link
Member

The changes of this PR will also need to be released as a hotfix in v8

@philprime philprime marked this pull request as draft November 3, 2025 13:29
@philprime
Copy link
Member

Converting back to draft because the sample did not properly mask.

@philprime
Copy link
Member

philprime commented Nov 3, 2025

I made masking stricter to stop semi‑transparent overlays from unmasking things underneath. Some overlays (like PopupDialog) add a see‑through tint as a child view on top of a clear container. Our old check could think the container was “opaque” and clear redactions behind it.

Now, a view only clears what’s behind it if it’s truly opaque:

  • layer opacity is 1
  • both the view and the layer have an opaque background (alpha == 1)
  • both report opaque

This protects privacy and worst case we do a bit less “clip‑out” optimization, leaving more masking in than necessary. I also updated tests to explicitly set opaque props when they expect clip‑out.

Next Step:

  • Evaluate masking in sample apps
  • Evaluate masking in real app (e.g. Flinky)
  • Merge into main, then merge into v8-x
  • Add a callout to the release notes so people know that masking might be stricter than before

@philprime philprime marked this pull request as ready for review November 4, 2025 09:43
Copy link
Contributor

@noahsmartin noahsmartin left a comment

Choose a reason for hiding this comment

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

I think this is an improvement, but I can still think of edge cases. For example what if you use UIColor(patternImage: ) I don't know what the opacity of this will be, but you could use an image that has some transparent regions. I wonder why we even need this, what if we just went ahead and masked on top of things even if they were covered by another view. I don't think that would hurt performance or anything a noticeable amount. IMO our long term strategy should be to do some simplifying things like that, rather than adding more edge cases

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(session-replay): Include layer background color when checking if a view is opaque

4 participants