From f16af4bd9b9d39cced508efa25d05f7ae3df5590 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Wed, 17 Dec 2025 17:06:41 +0100 Subject: [PATCH 1/2] feat(apple): Add documentation about view traversal options for session replay --- .../common/session-replay/customredact.mdx | 52 +++++++++++++++++++ .../common/session-replay/troubleshooting.mdx | 28 ++++++++++ 2 files changed, 80 insertions(+) diff --git a/docs/platforms/apple/common/session-replay/customredact.mdx b/docs/platforms/apple/common/session-replay/customredact.mdx index f5338add87c587..ec81078f4c2e58 100644 --- a/docs/platforms/apple/common/session-replay/customredact.mdx +++ b/docs/platforms/apple/common/session-replay/customredact.mdx @@ -93,6 +93,58 @@ var body: some View { } ``` +## Ignore View Types from Subtree Traversal + +Some view hierarchies contain layers that crash when accessed during traversal. For example, `CameraUI.ChromeSwiftUIView` on iOS 26+ contains `CameraUI.ModeLoupeLayer` instances that cause fatal errors when their `sublayers` property is accessed, resulting in crashes during session replay or screenshot capture. + +To prevent these crashes, you can configure the SDK to skip traversing the subtree of specific view types while still redacting the view itself if needed. + +### Default Behavior + +On iOS 26+, `CameraUI.ChromeSwiftUIView` is automatically included in the ignore set by default to prevent crashes. On other platforms or iOS versions, the ignore set is empty by default. + +### Adding View Types to Ignore List + +You can add custom view types to the ignore set using the `excludeViewTypeFromSubtreeTraversal` method: + +```swift +options.sessionReplay.excludeViewTypeFromSubtreeTraversal("MyProblematicView") +``` + +The view type identifier should match the result of `type(of: view).description()`, which typically follows the format `"ModuleName.ClassName"`. + +### Removing View Types from Ignore List + +If you need to remove a view type from the ignore set (not recommended for default values), you can use: + +```swift +options.sessionReplay.includeViewTypeInSubtreeTraversal("CameraUI.ChromeSwiftUIView") +``` + + + +Removing default values like `CameraUI.ChromeSwiftUIView` from the ignore set may cause crashes on iOS 26+. Only do this if you understand the risks and have tested thoroughly. + + + +### Dictionary Initialization + +You can also configure ignored view types when initializing `SentryReplayOptions` from a dictionary: + +```swift +let replayOptions = SentryReplayOptions(dictionary: [ + "viewTypesIgnoredFromSubtreeTraversal": ["MyCustomView", "AnotherProblematicView"] +]) +``` + +### How It Works + +When a view type is ignored from subtree traversal: + +- The view itself is still redacted (unless it's explicitly marked to be ignored, like `UISwitch`) +- The SDK skips traversing the view's subtree, preventing crashes from accessing problematic sublayers +- This prevents crashes while maintaining privacy protection for the view itself + ## Debugging Session Replay masking To see how elements are being masked, enable the masking preview from anywhere in your app. It will display an overlay on top of the masked elements. This works on the simulator and on device, as well as within Xcode Preview. diff --git a/docs/platforms/apple/common/session-replay/troubleshooting.mdx b/docs/platforms/apple/common/session-replay/troubleshooting.mdx index 54a66b7f4ed18b..9070c18a384bbe 100644 --- a/docs/platforms/apple/common/session-replay/troubleshooting.mdx +++ b/docs/platforms/apple/common/session-replay/troubleshooting.mdx @@ -161,6 +161,34 @@ final class SceneDelegate: NSObject, UIWindowSceneDelegate { + + +If your app crashes during Session Replay recording or screenshot capture, it may be due to problematic view hierarchies that contain layers that crash when accessed during traversal. + +### Common Causes + +Some view types contain layers that cause fatal errors when their `sublayers` property is accessed. For example, `CameraUI.ChromeSwiftUIView` on iOS 26+ contains `CameraUI.ModeLoupeLayer` instances that crash during view hierarchy traversal. + +### Solution: Ignore View Types from Subtree Traversal + +You can prevent these crashes by configuring the SDK to skip traversing the subtree of problematic view types. The view itself will still be redacted, but the SDK won't attempt to traverse its children, preventing crashes. + +To add a problematic view type to the ignore set: + +```swift +options.sessionReplay.excludeViewTypeFromSubtreeTraversal("MyProblematicView") +``` + +The view type identifier should match the result of `type(of: view).description()`. You can find the exact type name by inspecting the view hierarchy or checking crash logs. + +### Default Protection + +On iOS 26+, `CameraUI.ChromeSwiftUIView` is automatically included in the ignore set by default to prevent crashes. If you're experiencing crashes with other view types, add them to the ignore set using the method above. + +For more information, see the [Ignore View Types from Subtree Traversal](/platforms/apple/guides/ios/session-replay/customredact/#ignore-view-types-from-subtree-traversal) section in the custom masking documentation. + + + [AsyncDisplayKit](https://github.com/facebookarchive/AsyncDisplayKit) (now archived) and its successor [Texture](https://github.com/texturegroup/texture/) use a custom rendering pipeline that relies on layers rather than standard UIKit components. This architecture prevents Session Replay's default masking options (`maskAllImages` and `maskAllText`) from working correctly. From 406dd5d083d0b2f09c7d589ffadf84d87c1643ea Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Wed, 14 Jan 2026 12:41:41 +0100 Subject: [PATCH 2/2] docs(apple): Clarify view type identifier matching for session replay options Updated documentation to specify that view type identifiers use partial string matching for exclusion and exact string matching for inclusion. Added details on how the final set of excluded view types is computed, enhancing clarity on the session replay configuration process. --- .../apple/common/session-replay/customredact.mdx | 16 ++++++++++++++-- .../common/session-replay/troubleshooting.mdx | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/platforms/apple/common/session-replay/customredact.mdx b/docs/platforms/apple/common/session-replay/customredact.mdx index ec81078f4c2e58..8e8c5ff1e0b78c 100644 --- a/docs/platforms/apple/common/session-replay/customredact.mdx +++ b/docs/platforms/apple/common/session-replay/customredact.mdx @@ -111,7 +111,7 @@ You can add custom view types to the ignore set using the `excludeViewTypeFromSu options.sessionReplay.excludeViewTypeFromSubtreeTraversal("MyProblematicView") ``` -The view type identifier should match the result of `type(of: view).description()`, which typically follows the format `"ModuleName.ClassName"`. +The view type identifier uses **partial string matching** against the result of `type(of: view).description()`. This means if you exclude `"MyView"`, it will match `"MyApp.MyView"`, `"MyViewSubclass"`, `"Some.MyView.Container"`, etc. The view type identifier typically follows the format `"ModuleName.ClassName"`. ### Removing View Types from Ignore List @@ -121,6 +121,8 @@ If you need to remove a view type from the ignore set (not recommended for defau options.sessionReplay.includeViewTypeInSubtreeTraversal("CameraUI.ChromeSwiftUIView") ``` +The view type identifier uses **exact string matching** against the result of `type(of: view).description()`. This means `"MyApp.MyView"` will only match exactly `"MyApp.MyView"`, not `"MyApp.MyViewSubclass"`. This exact matching prevents accidental matches when removing view types from the ignore set. + Removing default values like `CameraUI.ChromeSwiftUIView` from the ignore set may cause crashes on iOS 26+. Only do this if you understand the risks and have tested thoroughly. @@ -133,12 +135,22 @@ You can also configure ignored view types when initializing `SentryReplayOptions ```swift let replayOptions = SentryReplayOptions(dictionary: [ - "viewTypesIgnoredFromSubtreeTraversal": ["MyCustomView", "AnotherProblematicView"] + "excludedViewClasses": ["MyCustomView", "AnotherProblematicView"], + "includedViewClasses": ["CameraUI.ChromeSwiftUIView"] ]) ``` +- `excludedViewClasses`: An array of view type identifier patterns that use partial matching (contains) +- `includedViewClasses`: An array of view type identifiers that use exact matching to remove items from the excluded set + ### How It Works +The final set of excluded view types is computed using the formula: **Default View Classes + Excluded View Classes - Included View Classes** + +- Default view classes are defined internally by the SDK (e.g., `CameraUI.ChromeSwiftUIView` on iOS 26+) +- Excluded view classes use partial matching (contains) - if a view's class name contains the pattern, it's excluded +- Included view classes use exact matching - they remove specific view types from the excluded set + When a view type is ignored from subtree traversal: - The view itself is still redacted (unless it's explicitly marked to be ignored, like `UISwitch`) diff --git a/docs/platforms/apple/common/session-replay/troubleshooting.mdx b/docs/platforms/apple/common/session-replay/troubleshooting.mdx index 9070c18a384bbe..e9938d66cafe82 100644 --- a/docs/platforms/apple/common/session-replay/troubleshooting.mdx +++ b/docs/platforms/apple/common/session-replay/troubleshooting.mdx @@ -179,7 +179,7 @@ To add a problematic view type to the ignore set: options.sessionReplay.excludeViewTypeFromSubtreeTraversal("MyProblematicView") ``` -The view type identifier should match the result of `type(of: view).description()`. You can find the exact type name by inspecting the view hierarchy or checking crash logs. +The view type identifier uses **partial string matching** against the result of `type(of: view).description()`. This means if you exclude `"MyView"`, it will match `"MyApp.MyView"`, `"MyViewSubclass"`, etc. You can find the exact type name by inspecting the view hierarchy or checking crash logs. ### Default Protection