View Hierarchy for iOS
Learn more about debugging the view hierarchy when an error occurs. Sentry pairs the view hierarchy representation with the original event, giving you additional insight into issues.
Sentry makes it possible to render a JSON representation of the view hierarchy of an error and includes it as an attachment.
This feature only applies to SDKs with a user interface, such as the ones for mobile and desktop applications. In some environments like native iOS, rendering the view hierarchy requires the UI thread and in the event of a crash, that might not be available. Another example where the view hierarchy might not be available is when the event happens before the screen starts to load. So inherently, this feature is a best effort solution.
App hang events will not have view hierarchy because the main thread is blocked and Sentry can't interact with UI elements in a background thread.
Deobfuscation for view hierarchies is fully supported for native SDKs and React Native, but is currently not supported for Flutter.
View hierarchy support for SwiftUI is highly limited, due to the SwiftUI rendering engine not backing every view with a UIKit view anymore.
For example, Text views may be rendered directly into a graphics render surface rather than creating a corresponding UILabel. This is an operating system limitation, not a limitation of the Sentry SDK. As a result, the captured view hierarchy may not fully represent the SwiftUI view tree.
View hierarchy debugging is an opt-in feature. You can enable it as shown below:
import Sentry
SentrySDK.start { options in
options.attachViewHierarchy = true
}
import Sentry
SentrySDK.start { options in
options.attachViewHierarchy = true
}
@import Sentry;
[SentrySDK startWithConfigureOptions:^(SentryOptions *options) {
options.attachViewHierarchy = YES;
}];
#import <SentryObjC/SentryObjC.h>
[SentryObjCSDK startWithConfigureOptions:^(SentryObjCOptions *options) {
options.attachViewHierarchy = YES;
}];
Requires Cocoa SDK version 8.33.0 or higher.
The beforeCaptureViewHierarchy also allows you to customize the behavior based on event data, so you can decide when to capture a view hierarchy and when not to. The callback doesn't work for crash events.
import Sentry
SentrySDK.start { options in
options.dsn = "___PUBLIC_DSN___"
options.beforeCaptureViewHierarchy = { _ in
// Return false to not capture a view hierarchy for the event.
return false
}
}
import Sentry
SentrySDK.start { options in
options.dsn = "___PUBLIC_DSN___"
options.beforeCaptureViewHierarchy = { _ in
// Return false to not capture a view hierarchy for the event.
return false
}
}
@import Sentry;
[SentrySDK startWithConfigureOptions:^(SentryOptions *options) {
options.dsn = @"___PUBLIC_DSN___";
options.beforeCaptureViewHierarchy = ^BOOL(SentryEvent * _Nonnull event) {
// Return NO to not capture a view hierarchy for the event.
return NO;
};
}];
#import <SentryObjC/SentryObjC.h>
[SentryObjCSDK startWithConfigureOptions:^(SentryObjCOptions *options) {
options.dsn = @"___PUBLIC_DSN___";
options.beforeCaptureViewHierarchy = ^BOOL(SentryObjCEvent * _Nonnull event) {
// Return NO to not capture a view hierarchy for the event.
return NO;
};
}];
Sentry doesn't collect any user information (such as UILabel and UITextView text) with the view hierarchy attachment, which only contains structure and properties.
If you're using the view's accessibilityIdentifier property with personal information, make sure to disable accessibilityIdentifier reporting like in the example below:
import Sentry
SentrySDK.start { options in
options.attachViewHierarchy = true
options.reportAccessibilityIdentifier = false
}
import Sentry
SentrySDK.start { options in
options.attachViewHierarchy = true
options.reportAccessibilityIdentifier = false
}
@import Sentry;
[SentrySDK startWithConfigureOptions:^(SentryOptions *options) {
options.attachViewHierarchy = YES;
options.reportAccessibilityIdentifier = NO;
}];
#import <SentryObjC/SentryObjC.h>
[SentryObjCSDK startWithConfigureOptions:^(SentryObjCOptions *options) {
options.attachViewHierarchy = YES;
options.reportAccessibilityIdentifier = NO;
}];
The SDK stores the hierarchy in a view-hierarchy.json attachment. The attachment uses the application/json content type and the event.view_hierarchy attachment type so Sentry can render it as an interactive hierarchy.
The following example contains one window, its view controller's root view, and a child view:
view-hierarchy.json{
"rendering_system": "UIKIT",
"windows": [
{
"type": "UIWindow",
"identifier": "main-window",
"width": 390,
"height": 844,
"x": 0,
"y": 0,
"alpha": 1,
"visible": true,
"children": [
{
"type": "UIView",
"view_controller": "CheckoutViewController",
"width": 390,
"height": 844,
"x": 0,
"y": 0,
"alpha": 1,
"visible": true,
"children": [
{
"type": "UIButton",
"identifier": "submit-order",
"width": 200,
"height": 44,
"x": 95,
"y": 720,
"alpha": 1,
"visible": true,
"children": []
}
]
}
]
}
]
}
{
"rendering_system": "UIKIT",
"windows": [
{
"type": "UIWindow",
"identifier": "main-window",
"width": 390,
"height": 844,
"x": 0,
"y": 0,
"alpha": 1,
"visible": true,
"children": [
{
"type": "UIView",
"view_controller": "CheckoutViewController",
"width": 390,
"height": 844,
"x": 0,
"y": 0,
"alpha": 1,
"visible": true,
"children": [
{
"type": "UIButton",
"identifier": "submit-order",
"width": 200,
"height": 44,
"x": 95,
"y": 720,
"alpha": 1,
"visible": true,
"children": []
}
]
}
]
}
]
}
The root object contains these fields:
| Field | Type | Description |
|---|---|---|
rendering_system | string | Rendering system that produced the hierarchy. The Apple SDK uses UIKIT. |
windows | array | Top-level window nodes. Each window uses the same recursive node structure as its descendants. |
Each window or child node, except truncation markers, contains these fields:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Runtime class name of the view, such as UIWindow, UIView, or UIButton. |
width | number | Yes | Width of the view's frame in points. |
height | number | Yes | Height of the view's frame in points. |
x | number | Yes | Horizontal origin of the view's frame in its containing coordinate space, in points. |
y | number | Yes | Vertical origin of the view's frame in its containing coordinate space, in points. |
alpha | number | Yes | Opacity of the view, from 0 for transparent to 1 for opaque. |
visible | boolean | Yes | Whether the view's isHidden property is false. This does not account for the visibility or alpha of ancestor views. |
children | array | Yes | Child views in UIKit subview order. An empty array indicates that the node has no child views. |
identifier | string | No | The view's accessibilityIdentifier. The SDK omits this field when the identifier is empty or reportAccessibilityIdentifier is disabled. |
view_controller | string | No | Runtime class name of the view controller when this node is the controller's root view. |
To keep captures bounded, the Apple SDK records up to 90 levels of nested views. When a view at that limit has child views, the SDK replaces those child views with a truncation marker:
{
"type": "SentryTruncatedViewHierarchy"
}
{
"type": "SentryTruncatedViewHierarchy"
}
Truncation markers contain only the type field. The other required fields in the table above do not apply to these markers.
View hierarchies appear in the "Attachments" tab, where you can view all attachments, as well as associated events. Click the event ID to open the Issue Details page of that specific event.
On the Issue Details page, you can interact with the view hierarchy attachment in a section called "View Hierarchy". This section represents the state of your application at the time of an error event. There are three displays: a tree view, wireframe, and detailed view for a selected node. Select a node in the tree or wireframe to inspect its JSON fields. Use these displays to debug layout issues, find unnecessarily rendered content, or understand the relationship between views.
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").