From 7140e19c849d23119aa30daa5d1ef615f9ea67ca Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Tue, 8 Oct 2024 16:42:18 +0200 Subject: [PATCH] fix: Add TTID/TTFD spans when loadView skipped The SDK creates a transaction when only viewDidLoad gets called for a UIViewController but doesn't create TTID/TTFD spans, leading to transactions missing TTID/TTFD data. Now, the SDK also creates TTID/TTFD spans when only viewDidLoad gets called and loadView gets skipped. Fixes GH-4396 --- CHANGELOG.md | 1 + .../ViewControllers/SplitViewController.swift | 1 - ...SentryUIViewControllerPerformanceTracker.m | 1 + ...iewControllerPerformanceTrackerTests.swift | 67 ++++++++++++++++--- 4 files changed, 61 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b97c1640b91..712ded02a75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Edge case for swizzleClassNameExclude (#4405): Skip creating transactions for UIViewControllers ignored for swizzling via the option `swizzleClassNameExclude`. +- Add TTID/TTFD spans when loadView gets skipped (#4415) ## 8.38.0-beta.1 diff --git a/Samples/iOS-Swift/iOS-Swift/ViewControllers/SplitViewController.swift b/Samples/iOS-Swift/iOS-Swift/ViewControllers/SplitViewController.swift index b7ad604e61c..1356fcca4f3 100644 --- a/Samples/iOS-Swift/iOS-Swift/ViewControllers/SplitViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ViewControllers/SplitViewController.swift @@ -66,7 +66,6 @@ class SecondarySplitViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - SentrySDK.reportFullyDisplayed() if let topvc = TopViewControllerInspector.shared { topvc.bringToFront() diff --git a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m index 200a6f933df..408356f11e9 100644 --- a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m +++ b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m @@ -102,6 +102,7 @@ - (void)viewControllerViewDidLoad:(UIViewController *)controller block:^{ SENTRY_LOG_DEBUG(@"Tracking viewDidLoad"); [self createTransaction:controller]; + [self createTimeToDisplay:controller]; [self measurePerformance:@"viewDidLoad" target:controller callbackToOrigin:callbackToOrigin]; diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift index 24fa222d4a3..31124a0a15c 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift @@ -576,7 +576,6 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { sut.enableWaitForFullDisplay = true - //The first view controller creates a transaction sut.viewControllerLoadView(firstController) { tracer = self.getStack(tracker).first as? SentryTracer } @@ -594,13 +593,64 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { sut.enableWaitForFullDisplay = false - //The first view controller creates a transaction sut.viewControllerLoadView(firstController) { tracer = self.getStack(tracker).first as? SentryTracer } XCTAssertEqual(tracer?.children.count, 2) } + + func test_OnlyViewDidLoad_CreatesTTIDSpan() throws { + let sut = fixture.getSut() + let tracker = fixture.tracker + + var tracer: SentryTracer! + + sut.viewControllerViewDidLoad(TestViewController()) { + tracer = self.getStack(tracker).first as? SentryTracer + } + + let children: [Span]? = Dynamic(tracer).children as [Span]? + + XCTAssertEqual(children?.count, 2) + + let firstChild = try XCTUnwrap(children?.first) + XCTAssertEqual("ui.load.initial_display", firstChild.operation) + XCTAssertEqual("TestViewController initial display", firstChild.spanDescription) + let secondChild = try XCTUnwrap(children?[1]) + XCTAssertEqual("ui.load", secondChild.operation) + XCTAssertEqual("viewDidLoad", secondChild.spanDescription) + } + + func test_OnlyLoadViewAndViewDidLoad_CreatesOneTTIDSpan() throws { + let sut = fixture.getSut() + let tracker = fixture.tracker + let controller = TestViewController() + var tracer: SentryTracer! + + sut.viewControllerLoadView(controller) { + tracer = self.getStack(tracker).first as? SentryTracer + } + + sut.viewControllerViewDidLoad(controller) { + // Empty on purpose + } + + let children: [Span]? = Dynamic(tracer).children as [Span]? + + XCTAssertEqual(children?.count, 3) + + let child1 = try XCTUnwrap(children?.first) + XCTAssertEqual("ui.load.initial_display", child1.operation) + + let child2 = try XCTUnwrap(children?[1]) + XCTAssertEqual("ui.load", child2.operation) + XCTAssertEqual("loadView", child2.spanDescription) + + let child3 = try XCTUnwrap(children?[2]) + XCTAssertEqual("ui.load", child3.operation) + XCTAssertEqual("viewDidLoad", child3.spanDescription) + } func test_captureAllAutomaticSpans() { let sut = fixture.getSut() @@ -643,12 +693,13 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { let children: [Span]? = Dynamic(tracer).children as [Span]? - //First Controller viewDidLoad - //Second Controller root span - //Second Controller viewDidLoad - //Third Controller root span - //Third Controller viewDidLoad - XCTAssertEqual(children?.count, 5) + // First Controller initial_display + // First Controller viewDidLoad + // Second Controller root span + // Second Controller viewDidLoad + // Third Controller root span + // Third Controller viewDidLoad + XCTAssertEqual(children?.count, 6) } private func assertSpanDuration(span: Span?, expectedDuration: TimeInterval) throws {