Skip to content

Commit

Permalink
feat: Enhance the UIViewController breadcrumbs with more data (#1945)
Browse files Browse the repository at this point in the history
We're adding more UIViewController information in the viewDidAppear breadcrumbs
  • Loading branch information
kevinrenskers committed Jul 13, 2022
1 parent 6ef0767 commit e65226c
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features

- Enhance the UIViewController breadcrumbs with more data (#1945)
- feat: Add extra app start span (#1952)
- Add enableAutoBreadcrumbTracking option (#1958)

Expand Down
2 changes: 1 addition & 1 deletion Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@
6308532C2440C44F00DDE4CE /* Sentry.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Sentry.xcodeproj; path = ../../Sentry.xcodeproj; sourceTree = "<group>"; };
637AFDA6243B02760034958B /* iOS-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; };
637AFDA9243B02760034958B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
637AFDAD243B02760034958B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
637AFDAD243B02760034958B /* ViewController.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; tabWidth = 4; };
637AFDB0243B02760034958B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
637AFDB2243B02770034958B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
637AFDB5243B02770034958B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
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 @@ -5,7 +5,7 @@ import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

static let defaultDSN = "https://a92d50327ac74b8b9aa4ea80eccfb267@o447951.ingest.sentry.io/5428557"

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Expand Down
14 changes: 11 additions & 3 deletions Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleAspectFit" axis="vertical" distribution="fillProportionally" translatesAutoresizingMaskIntoConstraints="NO" id="480-5y-FtF">
<rect key="frame" x="8" y="252.5" width="398" height="445"/>
<rect key="frame" x="8" y="242.5" width="398" height="465.5"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillProportionally" alignment="top" translatesAutoresizingMaskIntoConstraints="NO" id="VbH-LD-DBn">
<rect key="frame" x="0.0" y="0.0" width="398" height="300"/>
Expand Down Expand Up @@ -185,7 +185,7 @@
</subviews>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="UrL-kT-AJU">
<rect key="frame" x="0.0" y="300" width="398" height="145"/>
<rect key="frame" x="0.0" y="300" width="398" height="165.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="DSN " textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="m3h-wb-Xfa">
<rect key="frame" x="8" y="32" width="382" height="20.5"/>
Expand Down Expand Up @@ -215,6 +215,13 @@
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Last breadcrumb" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ccs-Ny-MiI" userLabel="breadcrumb">
<rect key="frame" x="8" y="137" width="382" height="20.5"/>
<accessibility key="accessibilityConfiguration" identifier="breadcrumbLabel"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<edgeInsets key="layoutMargins" top="32" left="8" bottom="8" right="8"/>
</stackView>
Expand All @@ -233,6 +240,7 @@
<connections>
<outlet property="anrFillingRunLoopButton" destination="TSF-10-5ts" id="Qp6-8q-IPH"/>
<outlet property="anrFullyBlockingButton" destination="Yi7-GN-26l" id="tMf-U3-onN"/>
<outlet property="breadcrumbLabel" destination="Ccs-Ny-MiI" id="WqJ-PA-KfJ"/>
<outlet property="dsnTextField" destination="L2a-LY-eQ7" id="TWn-0u-2v7"/>
<outlet property="framesLabel" destination="Lcm-n2-ys4" id="7mO-bc-HWw"/>
</connections>
Expand Down Expand Up @@ -372,7 +380,7 @@
</items>
</navigationBar>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" preservesSuperviewLayoutMargins="YES" layoutMarginsFollowReadableWidth="YES" delaysContentTouches="NO" editable="NO" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sla-j3-cfX">
<rect key="frame" x="20" y="70" width="374" height="826"/>
<rect key="frame" x="19" y="70" width="374" height="825"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<color key="textColor" systemColor="labelColor"/>
Expand Down
19 changes: 17 additions & 2 deletions Samples/iOS-Swift/iOS-Swift/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class ViewController: UIViewController {
@IBOutlet weak var anrFullyBlockingButton: UIButton!
@IBOutlet weak var anrFillingRunLoopButton: UIButton!
@IBOutlet weak var framesLabel: UILabel!
@IBOutlet weak var breadcrumbLabel: UILabel!

private let dispatchQueue = DispatchQueue(label: "ViewController")

Expand All @@ -18,6 +19,7 @@ class ViewController: UIViewController {
scope.setEnvironment("debug")
scope.setTag(value: "swift", key: "language")
scope.setExtra(value: String(describing: self), key: "currentViewController")

let user = Sentry.User(userId: "1")
user.email = "tony@example.com"
scope.setUser(user)
Expand All @@ -28,8 +30,8 @@ class ViewController: UIViewController {
if let data = "hello".data(using: .utf8) {
scope.add(Attachment(data: data, filename: "log.txt"))
}

}

// Also works
let user = Sentry.User(userId: "1")
user.email = "tony1@example.com"
Expand All @@ -43,7 +45,6 @@ class ViewController: UIViewController {
self.dsnTextField.backgroundColor = UIColor.systemGreen
}
}

}

override func viewDidAppear(_ animated: Bool) {
Expand All @@ -54,6 +55,20 @@ class ViewController: UIViewController {
self.framesLabel?.text = "Frames Total:\(PrivateSentrySDKOnly.currentScreenFrames.total) Slow:\(PrivateSentrySDKOnly.currentScreenFrames.slow) Frozen:\(PrivateSentrySDKOnly.currentScreenFrames.frozen)"
}
}

SentrySDK.configureScope { (scope) in
let dict = scope.serialize()

guard
let crumbs = dict["breadcrumbs"] as? [[String: Any]],
let breadcrumb = crumbs.last,
let data = breadcrumb["data"] as? [String: String]
else {
return
}

self.breadcrumbLabel.text = "{ category: \(breadcrumb["category"] ?? "nil"), parentViewController: \(data["parentViewController"] ?? "nil"), beingPresented: \(data["beingPresented"] ?? "nil"), window_isKeyWindow: \(data["window_isKeyWindow"] ?? "nil"), is_window_rootViewController: \(data["is_window_rootViewController"] ?? "nil") }"
}
}

@IBAction func addBreadcrumb(_ sender: Any) {
Expand Down
6 changes: 6 additions & 0 deletions Samples/iOS-Swift/iOS-SwiftUITests/LaunchUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ class LaunchUITests: XCTestCase {
super.tearDown()
}

func testBreadcrumbData() {
let breadcrumbLabel = app.staticTexts["breadcrumbLabel"]
XCTAssertTrue(breadcrumbLabel.waitForExistence(), "Breadcrumb label not found.")
XCTAssertEqual(breadcrumbLabel.label, "{ category: ui.lifecycle, parentViewController: UINavigationController, beingPresented: false, window_isKeyWindow: true, is_window_rootViewController: false }")
}

func testLoremIpsum() {
app.buttons["loremIpsumButton"].tap()
XCTAssertTrue(app.textViews.firstMatch.waitForExistence(), "Lorem Ipsum not loaded.")
Expand Down
43 changes: 39 additions & 4 deletions Sources/Sentry/SentryBreadcrumbTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,12 @@ - (void)swizzleViewDidAppear
SentryBreadcrumb *crumb = [[SentryBreadcrumb alloc] initWithLevel:kSentryLevelInfo
category:@"ui.lifecycle"];
crumb.type = @"navigation";
NSString *viewControllerName = [SentryUIViewControllerSanitizer
sanitizeViewControllerName:[NSString stringWithFormat:@"%@", self]];
crumb.data = @ { @"screen" : viewControllerName };
crumb.data = [SentryBreadcrumbTracker fetchInfoAboutViewController:self];

// Adding crumb via the SDK calls SentryBeforeBreadcrumbCallback
[SentrySDK addBreadcrumb:crumb];
[SentrySDK.currentHub configureScope:^(SentryScope *_Nonnull scope) {
[scope setExtraValue:viewControllerName forKey:@"__sentry_transaction"];
[scope setExtraValue:crumb.data[@"screen"] forKey:@"__sentry_transaction"];
}];
}
SentrySWCallOriginal(animated);
Expand Down Expand Up @@ -236,6 +234,43 @@ + (NSDictionary *)extractDataFromView:(UIView *)view

return result;
}

+ (NSDictionary *)fetchInfoAboutViewController:(UIViewController *)controller
{
NSMutableDictionary *info = @{}.mutableCopy;

info[@"screen"] = [SentryUIViewControllerSanitizer
sanitizeViewControllerName:[NSString stringWithFormat:@"%@", controller]];

if ([controller.navigationItem.title length] != 0) {
info[@"title"] = controller.navigationItem.title;
} else if ([controller.title length] != 0) {
info[@"title"] = controller.title;
}

info[@"beingPresented"] = controller.beingPresented ? @"true" : @"false";

if (controller.presentingViewController != nil) {
info[@"presentingViewController"] = [SentryUIViewControllerSanitizer
sanitizeViewControllerName:controller.presentingViewController];
}

if (controller.parentViewController != nil) {
info[@"parentViewController"] = [SentryUIViewControllerSanitizer
sanitizeViewControllerName:controller.parentViewController];
}

if (controller.view.window != nil) {
info[@"window"] = controller.view.window.description;
info[@"window_isKeyWindow"] = controller.view.window.isKeyWindow ? @"true" : @"false";
info[@"window_windowLevel"] =
[NSString stringWithFormat:@"%f", controller.view.window.windowLevel];
info[@"is_window_rootViewController"]
= controller.view.window.rootViewController == controller ? @"true" : @"false";
}

return info;
}
#endif

@end
Expand Down
1 change: 0 additions & 1 deletion Sources/Sentry/include/SentryUIViewControllerSanitizer.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,24 @@ class SentryBreadcrumbTrackerTests: XCTestCase {
let sut = SentryBreadcrumbTracker(swizzleWrapper: SentrySwizzleWrapper.sharedInstance)
sut.start()
sut.startSwizzle()

let viewController = UIViewController()
_ = UINavigationController(rootViewController: viewController)
viewController.title = "test title"
viewController.viewDidAppear(false)

let crumbs = Dynamic(scope).breadcrumbArray.asArray as? [Breadcrumb]

XCTAssertEqual(2, crumbs?.count)

let lifeCycleCrumb = crumbs?[1]
XCTAssertEqual("navigation", lifeCycleCrumb?.type)
XCTAssertEqual("ui.lifecycle", lifeCycleCrumb?.category)
XCTAssertEqual("false", lifeCycleCrumb?.data?["beingPresented"] as? String)
XCTAssertEqual("UIViewController", lifeCycleCrumb?.data?["screen"] as? String)
XCTAssertEqual("test title", lifeCycleCrumb?.data?["title"] as? String)
XCTAssertEqual("false", lifeCycleCrumb?.data?["beingPresented"] as? String)
XCTAssertEqual("UINavigationController", lifeCycleCrumb?.data?["parentViewController"] as? String)
}

func testExtractDataFrom_View() {
Expand Down

0 comments on commit e65226c

Please sign in to comment.