From a689d580491cb97168a4c5d20b3c1a3a2d32b30e Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Mon, 2 Dec 2024 16:25:02 +0100 Subject: [PATCH 01/12] wip --- CHANGELOG.md | 1 - Sources/Sentry/SentryTimeToDisplayTracker.m | 21 +-- ...SentryUIViewControllerPerformanceTracker.m | 10 +- .../include/SentryTimeToDisplayTracker.h | 9 +- ...SentryUIViewControllerPerformanceTracker.h | 3 + .../SentryInternal/SentryInternal.h | 49 ++++++- Sources/SentrySwiftUI/SentryTracedView.swift | 135 +++++++++++++----- 7 files changed, 174 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42433a5dd9c..9e6b4a83bad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,6 @@ - Load integration from same binary (#4541) - Masking for fast animations #4574 - ### Improvements - impr: Speed up getBinaryImages V2 (#4539). Follow up on (#4435) diff --git a/Sources/Sentry/SentryTimeToDisplayTracker.m b/Sources/Sentry/SentryTimeToDisplayTracker.m index 242ad76db5e..8703d482949 100644 --- a/Sources/Sentry/SentryTimeToDisplayTracker.m +++ b/Sources/Sentry/SentryTimeToDisplayTracker.m @@ -36,15 +36,15 @@ @implementation SentryTimeToDisplayTracker { BOOL _waitForFullDisplay; BOOL _initialDisplayReported; BOOL _fullyDisplayedReported; - NSString *_controllerName; + NSString *_name; } -- (instancetype)initForController:(UIViewController *)controller - waitForFullDisplay:(BOOL)waitForFullDisplay - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper +- (instancetype)initWithName:(NSString *)name + waitForFullDisplay:(BOOL)waitForFullDisplay + dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper { if (self = [super init]) { - _controllerName = [SwiftDescriptor getObjectClassName:controller]; + _name = name; _waitForFullDisplay = waitForFullDisplay; _dispatchQueueWrapper = dispatchQueueWrapper; _initialDisplayReported = NO; @@ -53,6 +53,11 @@ - (instancetype)initForController:(UIViewController *)controller return self; } +- (instancetype)initWithName:(NSString *)name + waitForFullDisplay:(BOOL)waitForFullDisplay { + return [self initWithName:name waitForFullDisplay:waitForFullDisplay dispatchQueueWrapper:SentryDependencyContainer.sharedInstance.dispatchQueueWrapper]; +} + - (BOOL)startForTracer:(SentryTracer *)tracer { if (SentryDependencyContainer.sharedInstance.framesTracker.isRunning == NO) { @@ -64,7 +69,7 @@ - (BOOL)startForTracer:(SentryTracer *)tracer SENTRY_LOG_DEBUG(@"Starting initial display span"); self.initialDisplaySpan = [tracer startChildWithOperation:SentrySpanOperationUILoadInitialDisplay - description:[NSString stringWithFormat:@"%@ initial display", _controllerName]]; + description:[NSString stringWithFormat:@"%@ initial display", _name]]; self.initialDisplaySpan.origin = SentryTraceOriginAutoUITimeToDisplay; if (self.waitForFullDisplay) { @@ -72,7 +77,7 @@ - (BOOL)startForTracer:(SentryTracer *)tracer self.fullDisplaySpan = [tracer startChildWithOperation:SentrySpanOperationUILoadFullDisplay description:[NSString stringWithFormat:@"%@ full display", - _controllerName]]; + _name]]; self.fullDisplaySpan.origin = SentryTraceOriginManualUITimeToDisplay; // By concept TTID and TTFD spans should have the same beginning, @@ -146,7 +151,7 @@ - (void)finishSpansIfNotFinished if (self.fullDisplaySpan.isFinished == NO) { SENTRY_LOG_WARN(@"You didn't call SentrySDK.reportFullyDisplayed() for UIViewController: " @"%@. Finishing full display span with status: %@.", - _controllerName, nameForSentrySpanStatus(kSentrySpanStatusDeadlineExceeded)); + _name, nameForSentrySpanStatus(kSentrySpanStatusDeadlineExceeded)); [self.fullDisplaySpan finishWithStatus:kSentrySpanStatusDeadlineExceeded]; } diff --git a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m index 513f2a66538..a41d273c476 100644 --- a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m +++ b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m @@ -166,9 +166,9 @@ - (void)startRootSpanFor:(UIViewController *)controller [self.currentTTDTracker finishSpansIfNotFinished]; SentryTimeToDisplayTracker *ttdTracker = - [[SentryTimeToDisplayTracker alloc] initForController:controller - waitForFullDisplay:self.enableWaitForFullDisplay - dispatchQueueWrapper:_dispatchQueueWrapper]; + [[SentryTimeToDisplayTracker alloc] initWithName: [SwiftDescriptor getObjectClassName:controller] + waitForFullDisplay:self.enableWaitForFullDisplay + dispatchQueueWrapper:_dispatchQueueWrapper]; if ([ttdTracker startForTracer:(SentryTracer *)vcSpan]) { objc_setAssociatedObject(controller, &SENTRY_UI_PERFORMANCE_TRACKER_TTD_TRACKER, ttdTracker, @@ -184,6 +184,10 @@ - (void)reportFullyDisplayed [self.currentTTDTracker reportFullyDisplayed]; } +- (void)setTimeToDisplayTracker:(SentryTimeToDisplayTracker *)ttdTracker { + self.currentTTDTracker = ttdTracker; +} + - (void)viewControllerViewWillAppear:(UIViewController *)controller callbackToOrigin:(void (^)(void))callbackToOrigin { diff --git a/Sources/Sentry/include/SentryTimeToDisplayTracker.h b/Sources/Sentry/include/SentryTimeToDisplayTracker.h index 06a3a25a1ff..a5d39d90bb1 100644 --- a/Sources/Sentry/include/SentryTimeToDisplayTracker.h +++ b/Sources/Sentry/include/SentryTimeToDisplayTracker.h @@ -25,9 +25,12 @@ SENTRY_NO_INIT @property (nonatomic, readonly) BOOL waitForFullDisplay; -- (instancetype)initForController:(UIViewController *)controller - waitForFullDisplay:(BOOL)waitForFullDisplay - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; +- (instancetype)initWithName:(NSString *)name + waitForFullDisplay:(BOOL)waitForFullDisplay + dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; + +- (instancetype)initWithName:(NSString *)name + waitForFullDisplay:(BOOL)waitForFullDisplay; - (BOOL)startForTracer:(SentryTracer *)tracer; diff --git a/Sources/Sentry/include/SentryUIViewControllerPerformanceTracker.h b/Sources/Sentry/include/SentryUIViewControllerPerformanceTracker.h index ed520b34f52..ef8f28b239a 100644 --- a/Sources/Sentry/include/SentryUIViewControllerPerformanceTracker.h +++ b/Sources/Sentry/include/SentryUIViewControllerPerformanceTracker.h @@ -4,6 +4,7 @@ @class SentrySpan; @class SentryInAppLogic; +@class SentryTimeToDisplayTracker; @class UIViewController; NS_ASSUME_NONNULL_BEGIN @@ -101,6 +102,8 @@ static NSString *const SENTRY_UI_PERFORMANCE_TRACKER_TTD_TRACKER - (void)reportFullyDisplayed; +- (void)setTimeToDisplayTracker:(SentryTimeToDisplayTracker *)ttdTracker; + @end NS_ASSUME_NONNULL_END diff --git a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h index ce265ced396..2d4538d8070 100644 --- a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h +++ b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h @@ -7,12 +7,22 @@ #import +#if __has_include() +#import +#elif __has_include("Sentry.h") +#import "Sentry.h" +#endif + NS_ASSUME_NONNULL_BEGIN typedef NS_ENUM(NSInteger, SentryTransactionNameSource); @class SentrySpanId; -@protocol SentrySpan; +@class SentrySpan; + +@interface SentryTracer : NSObject +@end + typedef NS_ENUM(NSUInteger, SentrySpanStatus); @@ -56,4 +66,41 @@ typedef NS_ENUM(NSUInteger, SentrySpanStatus); @end +@interface SentryTimeToDisplayTracker : NSObject +-(instancetype)init NS_UNAVAILABLE; ++(instancetype) new NS_UNAVAILABLE; + +@property (nullable, nonatomic, weak, readonly) SentrySpan *initialDisplaySpan; + +@property (nullable, nonatomic, weak, readonly) SentrySpan *fullDisplaySpan; + +@property (nonatomic, readonly) BOOL waitForFullDisplay; + +- (instancetype)initWithName:(NSString *)name + waitForFullDisplay:(BOOL)waitForFullDisplay + dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; + +- (instancetype)initWithName:(NSString *)name + waitForFullDisplay:(BOOL)waitForFullDisplay; + +- (BOOL)startForTracer:(SentryTracer *)tracer; + +- (void)reportInitialDisplay; + +- (void)reportFullyDisplayed; + +- (void)finishSpansIfNotFinished; + +@end + +@interface SentryUIViewControllerPerformanceTracker : NSObject + +@property (nonatomic, readonly, class) SentryUIViewControllerPerformanceTracker *shared; + +- (void)reportFullyDisplayed; + +- (void)setTimeToDisplayTracker:(SentryTimeToDisplayTracker *)ttdTracker; + +@end + NS_ASSUME_NONNULL_END diff --git a/Sources/SentrySwiftUI/SentryTracedView.swift b/Sources/SentrySwiftUI/SentryTracedView.swift index 97e94ea9367..b74edd3e720 100644 --- a/Sources/SentrySwiftUI/SentryTracedView.swift +++ b/Sources/SentrySwiftUI/SentryTracedView.swift @@ -1,15 +1,51 @@ #if canImport(SwiftUI) + import Foundation -#if SENTRY_NO_UIKIT -import SentryWithoutUIKit -#else -import Sentry -#endif #if CARTHAGE || SWIFT_PACKAGE @_implementationOnly import SentryInternal +import Sentry #endif import SwiftUI +@available(iOS 13, macOS 10.15, tvOS 13, *) +struct SentryTrackView: UIViewRepresentable { + + let name : String + let waitForFullDisplay : Bool + + class SentryView: UIView { + + let name : String + let waitForFullDisplay : Bool + + init(name: String, waitForFullDisplay: Bool) { + self.name = name + self.waitForFullDisplay = waitForFullDisplay + super.init(frame: CGRect(origin: .zero, size: CGSize(width: 1, height: 1))) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func didMoveToWindow() { + super.didMoveToWindow() + + let ttdTracker = SentryTimeToDisplayTracker(name: "", waitForFullDisplay: true) + print(ttdTracker); + } + } + + func makeUIView(context: Context) -> UIView { + let view = SentryView(name: name, waitForFullDisplay: waitForFullDisplay) + view.isUserInteractionEnabled = false + return view + } + + func updateUIView(_ uiView: UIView, context: Context) { + } +} + /// A control to measure the performance of your views and send the result as a transaction to Sentry.io. /// /// You create a transaction by wrapping your views with this. @@ -39,61 +75,84 @@ import SwiftUI @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6.0, *) public struct SentryTracedView: View { @State var viewAppeared = false - + let content: () -> Content let name: String let nameSource: SentryTransactionNameSource let traceOrigin = "auto.ui.swift_ui" - + public init(_ viewName: String? = nil, content: @escaping () -> Content) { self.content = content self.name = viewName ?? SentryTracedView.extractName(content: Content.self) self.nameSource = viewName == nil ? .component : .custom } - + private static func extractName(content: Any) -> String { var result = String(describing: content) - + if let index = result.firstIndex(of: "<") { result = String(result[result.startIndex ..< index]) } - + return result } - + public var body: some View { - if viewAppeared { - return self.content().onAppear() + let content = !viewAppeared ? content() : tracedContent() + return content.onAppear { + viewAppeared = true } - - var transactionCreated = false - if SentryPerformanceTracker.shared.activeSpanId() == nil { - transactionCreated = true - let transactionId = SentryPerformanceTracker.shared.startSpan(withName: self.name, nameSource: nameSource, operation: "ui.load", origin: self.traceOrigin) - SentryPerformanceTracker.shared.pushActiveSpan(transactionId) - - //According to Apple's documentation, the call to `body` needs to be fast - //and can be made many times in one frame. Therefore they don't use async code to process the view. - //Scheduling to finish the transaction at the end of the main loop seems the least hack solution right now. - //'onAppear' is not a suitable place to do this because it may happen before other view `body` property get called. - DispatchQueue.main.async { - SentryPerformanceTracker.shared.popActiveSpan() - SentryPerformanceTracker.shared.finishSpan(transactionId) - } - } - - let id = SentryPerformanceTracker.shared.startSpan(withName: transactionCreated ? "\(self.name) - body" : self.name, nameSource: nameSource, operation: "ui.load", origin: self.traceOrigin) - - SentryPerformanceTracker.shared.pushActiveSpan(id) + } + + private func tracedContent() -> Content { + let transactionCreated = ensureTransactionExists() + + let spanId = createAndPushBodySpan(transactionCreated: transactionCreated) defer { - SentryPerformanceTracker.shared.popActiveSpan() - SentryPerformanceTracker.shared.finishSpan(id) + finishSpan(spanId) } - - return self.content().onAppear { - self.viewAppeared = true + + return content() + } + + private func ensureTransactionExists() -> Bool { + guard SentryPerformanceTracker.shared.activeSpanId() == nil else { return false } + + let transactionId = SentryPerformanceTracker.shared.startSpan( + withName: name, + nameSource: nameSource, + operation: "ui.load", + origin: traceOrigin + ) + SentryPerformanceTracker.shared.pushActiveSpan(transactionId) + + //According to Apple's documentation, the call to body needs to be fast + //and can be made many times in one frame. Therefore they don't use async code to process the view. + //Scheduling to finish the transaction at the end of the main loop seems the least hack solution right now. + //'onAppear' is not a suitable place to do this because it may happen before other view body property get called. + DispatchQueue.main.async { + self.finishSpan(transactionId) } + + return true + } + + private func createAndPushBodySpan(transactionCreated: Bool) -> SpanId { + let spanName = transactionCreated ? "\(name) - body" : name + let spanId = SentryPerformanceTracker.shared.startSpan( + withName: spanName, + nameSource: nameSource, + operation: "ui.load", + origin: traceOrigin + ) + SentryPerformanceTracker.shared.pushActiveSpan(spanId) + return spanId + } + + private func finishSpan(_ spanId: SpanId) { + SentryPerformanceTracker.shared.popActiveSpan() + SentryPerformanceTracker.shared.finishSpan(spanId) } } From fd905daff59254c873fb7c1e3f46cffa65b176e4 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 4 Dec 2024 12:42:34 +0100 Subject: [PATCH 02/12] more changes --- .../SentryRedactModifierTests.swift | 0 .../iOS-SwiftUI.xcodeproj/project.pbxproj | 19 +++++ .../iOS-SwiftUI/iOS-SwiftUI/ContentView.swift | 12 ++- Sentry.xcodeproj/project.pbxproj | 33 +------- Sources/Sentry/SentryHub.m | 6 +- .../SentryPerformanceTrackingIntegration.m | 2 +- Sources/Sentry/SentryProfiler.mm | 2 +- ...SentryUIViewControllerPerformanceTracker.m | 15 +++- ...SentryUIViewControllerPerformanceTracker.h | 2 +- .../SentryInternal/SentryInternal.h | 5 ++ Sources/SentrySwiftUI/SentryTracedView.swift | 80 +++++++------------ Sources/SentrySwiftUI/TracingView.swift | 59 ++++++++++++++ .../SentryAppLaunchProfilingTests.swift | 8 +- .../SentryTimeToDisplayTrackerTest.swift | 4 +- Tests/SentryTests/SentryHubTests.swift | 11 +-- 15 files changed, 151 insertions(+), 107 deletions(-) rename {Tests/SentryTests/SwiftUI => Samples/iOS-SwiftUI/iOS-SwiftUI-UITests}/SentryRedactModifierTests.swift (100%) create mode 100644 Sources/SentrySwiftUI/TracingView.swift diff --git a/Tests/SentryTests/SwiftUI/SentryRedactModifierTests.swift b/Samples/iOS-SwiftUI/iOS-SwiftUI-UITests/SentryRedactModifierTests.swift similarity index 100% rename from Tests/SentryTests/SwiftUI/SentryRedactModifierTests.swift rename to Samples/iOS-SwiftUI/iOS-SwiftUI-UITests/SentryRedactModifierTests.swift diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI.xcodeproj/project.pbxproj b/Samples/iOS-SwiftUI/iOS-SwiftUI.xcodeproj/project.pbxproj index 1d1ef792ba6..7149192537c 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI.xcodeproj/project.pbxproj +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI.xcodeproj/project.pbxproj @@ -21,6 +21,9 @@ D8199DCE29376FD90074249E /* SentrySwiftUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D8BBD38B2901AE400011F850 /* SentrySwiftUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D832FAF02982A908007A9A5F /* FormScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D832FAEF2982A908007A9A5F /* FormScreen.swift */; }; D85388D12980222500B63908 /* UIKitScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85388D02980222500B63908 /* UIKitScreen.swift */; }; + D8F0F3C02D0068A100826CE3 /* SentrySwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8BBD38B2901AE400011F850 /* SentrySwiftUI.framework */; }; + D8F0F3C12D0068A100826CE3 /* SentrySwiftUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D8BBD38B2901AE400011F850 /* SentrySwiftUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8F0F3C42D0068AB00826CE3 /* SentryRedactModifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0F3C32D0068AB00826CE3 /* SentryRedactModifierTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -130,6 +133,17 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + D8F0F3C22D0068A100826CE3 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D8F0F3C12D0068A100826CE3 /* SentrySwiftUI.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -159,6 +173,7 @@ D8A22A7C2915231F006907D9 /* SentrySpanStatus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentrySpanStatus.h; sourceTree = ""; }; D8A22A7D2915238A006907D9 /* SentryTracer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryTracer.h; path = ../../../../Sources/Sentry/include/SentryTracer.h; sourceTree = ""; }; D8A22A7E2915238A006907D9 /* SentryPerformanceTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryPerformanceTracker.h; path = ../../../../Sources/Sentry/include/SentryPerformanceTracker.h; sourceTree = ""; }; + D8F0F3C32D0068AB00826CE3 /* SentryRedactModifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryRedactModifierTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -166,6 +181,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D8F0F3C02D0068A100826CE3 /* SentrySwiftUI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -198,6 +214,7 @@ isa = PBXGroup; children = ( 7B64385926A6C0A6000D0F65 /* LaunchUITests.swift */, + D8F0F3C32D0068AB00826CE3 /* SentryRedactModifierTests.swift */, 7B64385B26A6C0A6000D0F65 /* Info.plist */, ); path = "iOS-SwiftUI-UITests"; @@ -306,6 +323,7 @@ 7B64385326A6C0A6000D0F65 /* Sources */, 7B64385426A6C0A6000D0F65 /* Frameworks */, 7B64385526A6C0A6000D0F65 /* Resources */, + D8F0F3C22D0068A100826CE3 /* Embed Frameworks */, ); buildRules = ( ); @@ -535,6 +553,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8F0F3C42D0068AB00826CE3 /* SentryRedactModifierTests.swift in Sources */, 7B64385A26A6C0A6000D0F65 /* LaunchUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift b/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift index be7b0b0f7ff..07608490656 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift @@ -115,12 +115,12 @@ struct ContentView: View { } var body: some View { - return SentryTracedView("Content View Body") { - NavigationView { + return SentryTracedView("Content View Body", waitForFullDisplay: true) { NavigationView { VStack(alignment: HorizontalAlignment.center, spacing: 16) { Group { Text(getCurrentTracer()?.transactionContext.name ?? "NO SPAN") .accessibilityIdentifier("TRANSACTION_NAME") + Text(getCurrentTracer()?.transactionContext.spanId.sentrySpanIdString ?? "NO ID") .accessibilityIdentifier("TRANSACTION_ID") .sentryReplayMask() @@ -128,6 +128,14 @@ struct ContentView: View { Text(getCurrentTracer()?.transactionContext.origin ?? "NO ORIGIN") .accessibilityIdentifier("TRACE_ORIGIN") }.sentryReplayUnmask() + .onAppear { + Task { + if #available(iOS 16.0, *) { + try? await Task.sleep(for: .seconds(2)) + } + SentrySDK.reportFullyDisplayed() + } + } SentryTracedView("Child Span") { VStack { Text(getCurrentSpan()?.spanDescription ?? "NO SPAN") diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index a0441c02971..c44f502fc34 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -830,9 +830,6 @@ D84F833E2A1CC401005828E0 /* SentrySwiftAsyncIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F833C2A1CC401005828E0 /* SentrySwiftAsyncIntegration.m */; }; D851527F2C9971020070F669 /* SentryStringUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D851527E2C9971020070F669 /* SentryStringUtils.h */; }; D85152832C997A280070F669 /* SentryStringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85152822C997A1F0070F669 /* SentryStringUtils.swift */; }; - D85153002CA2B5F60070F669 /* SentrySwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8199DAA29376E9B0074249E /* SentrySwiftUI.framework */; }; - D85153012CA2B5F60070F669 /* SentrySwiftUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D8199DAA29376E9B0074249E /* SentrySwiftUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D851530C2CA2B7B00070F669 /* SentryRedactModifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D851530B2CA2B7A30070F669 /* SentryRedactModifierTests.swift */; }; D85596F3280580F10041FF8B /* SentryScreenshotIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = D85596F1280580F10041FF8B /* SentryScreenshotIntegration.m */; }; D855AD62286ED6A4002573E1 /* SentryCrashTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D855AD61286ED6A4002573E1 /* SentryCrashTests.m */; }; D855B3E827D652AF00BCED76 /* SentryCoreDataTrackingIntegrationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D855B3E727D652AF00BCED76 /* SentryCoreDataTrackingIntegrationTest.swift */; }; @@ -928,6 +925,7 @@ D8DBE0D22C0EFFC300FAB1FD /* SentryReplayOptionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8DBE0D12C0EFFC300FAB1FD /* SentryReplayOptionsTests.swift */; }; D8F016B32B9622D6007B9AFB /* SentryId.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F016B22B9622D6007B9AFB /* SentryId.swift */; }; D8F016B62B962548007B9AFB /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F016B52B962548007B9AFB /* StringExtensions.swift */; }; + D8F0F3B72D005B0400826CE3 /* TracingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0F3B62D005AFB00826CE3 /* TracingView.swift */; }; D8F67AEE2BE0D19200C9197B /* UIImageHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F67AED2BE0D19200C9197B /* UIImageHelper.swift */; }; D8F67AF12BE0D33F00C9197B /* UIImageHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F67AEF2BE0D31A00C9197B /* UIImageHelperTests.swift */; }; D8F67AF42BE10F9600C9197B /* UIRedactBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F67AF22BE10F7600C9197B /* UIRedactBuilderTests.swift */; }; @@ -977,13 +975,6 @@ remoteGlobalIDString = D84DAD4C2B17428D003CF120; remoteInfo = SentryTestUtilsDynamic; }; - D85153022CA2B5F60070F669 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6327C5CA1EB8A783004E799B /* Project object */; - proxyType = 1; - remoteGlobalIDString = D8199DA929376E9B0074249E; - remoteInfo = SentrySwiftUI; - }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -994,7 +985,6 @@ dstSubfolderSpec = 10; files = ( D84DAD5A2B1742C1003CF120 /* SentryTestUtilsDynamic.framework in Embed Frameworks */, - D85153012CA2B5F60070F669 /* SentrySwiftUI.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -1908,7 +1898,6 @@ D8511F722BAC8F750015E6FD /* Sentry.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = Sentry.modulemap; sourceTree = ""; }; D851527E2C9971020070F669 /* SentryStringUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryStringUtils.h; sourceTree = ""; }; D85152822C997A1F0070F669 /* SentryStringUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryStringUtils.swift; sourceTree = ""; }; - D851530B2CA2B7A30070F669 /* SentryRedactModifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryRedactModifierTests.swift; sourceTree = ""; }; D85596F1280580F10041FF8B /* SentryScreenshotIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryScreenshotIntegration.m; sourceTree = ""; }; D855AD61286ED6A4002573E1 /* SentryCrashTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryCrashTests.m; sourceTree = ""; }; D855B3E727D652AF00BCED76 /* SentryCoreDataTrackingIntegrationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryCoreDataTrackingIntegrationTest.swift; sourceTree = ""; }; @@ -2018,6 +2007,7 @@ D8F016B52B962548007B9AFB /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = ""; }; D8F01DE42A126B62008F4996 /* HybridPod.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = HybridPod.podspec; sourceTree = ""; }; D8F01DE52A126BF5008F4996 /* HybridTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HybridTest.swift; sourceTree = ""; }; + D8F0F3B62D005AFB00826CE3 /* TracingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingView.swift; sourceTree = ""; }; D8F67AED2BE0D19200C9197B /* UIImageHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageHelper.swift; sourceTree = ""; }; D8F67AEF2BE0D31A00C9197B /* UIImageHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageHelperTests.swift; sourceTree = ""; }; D8F67AF22BE10F7600C9197B /* UIRedactBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIRedactBuilderTests.swift; sourceTree = ""; }; @@ -2049,7 +2039,6 @@ 8431F01C29B2854200D8DC56 /* libSentryTestUtils.a in Frameworks */, D84DAD592B1742C1003CF120 /* SentryTestUtilsDynamic.framework in Frameworks */, 63AA766A1EB8CB2F00D153DE /* Sentry.framework in Frameworks */, - D85153002CA2B5F60070F669 /* SentrySwiftUI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2545,7 +2534,6 @@ 7BF536D224BEF240004FA6A2 /* TestUtils */, D81FDF0F280E9FEC0045E0E4 /* Tools */, 7B6C5ED4264E62B60010D138 /* Transaction */, - D851530A2CA2B7960070F669 /* SwiftUI */, ); path = SentryTests; sourceTree = ""; @@ -3689,6 +3677,7 @@ D8199DB529376ECC0074249E /* SentrySwiftUI.h */, D88D25E92B8E0BAC0073C3D5 /* module.modulemap */, D8199DB629376ECC0074249E /* SentryTracedView.swift */, + D8F0F3B62D005AFB00826CE3 /* TracingView.swift */, D8A65B5C2C98656000974B74 /* SentryReplayView.swift */, ); path = SentrySwiftUI; @@ -3733,14 +3722,6 @@ path = SentryTestUtilsDynamic; sourceTree = ""; }; - D851530A2CA2B7960070F669 /* SwiftUI */ = { - isa = PBXGroup; - children = ( - D851530B2CA2B7A30070F669 /* SentryRedactModifierTests.swift */, - ); - path = SwiftUI; - sourceTree = ""; - }; D85596EF280580BE0041FF8B /* Screenshot */ = { isa = PBXGroup; children = ( @@ -4344,7 +4325,6 @@ dependencies = ( 63AA766C1EB8CB2F00D153DE /* PBXTargetDependency */, D84DAD5C2B1742C1003CF120 /* PBXTargetDependency */, - D85153032CA2B5F60070F669 /* PBXTargetDependency */, ); name = SentryTests; packageProductDependencies = ( @@ -5044,7 +5024,6 @@ D8AE48C12C57B1550092A2A6 /* SentryLevelTests.swift in Sources */, 63FE721820DA66EC00CDBAE8 /* TestThread.m in Sources */, 7B4D308A26FC616B00C94DE9 /* SentryHttpTransportTests.swift in Sources */, - D851530C2CA2B7B00070F669 /* SentryRedactModifierTests.swift in Sources */, 7B4E23B6251A07BD00060D68 /* SentryDispatchQueueWrapperTests.swift in Sources */, 63FE720720DA66EC00CDBAE8 /* SentryCrashReportFilter_Tests.m in Sources */, 8F73BC312B02B87E00C3CEF4 /* SentryInstallationTests.swift in Sources */, @@ -5171,6 +5150,7 @@ files = ( D8199DC129376EEC0074249E /* SentryTracedView.swift in Sources */, D8199DBF29376EE20074249E /* SentryInternal.m in Sources */, + D8F0F3B72D005B0400826CE3 /* TracingView.swift in Sources */, D8A65B5D2C98656800974B74 /* SentryReplayView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -5211,11 +5191,6 @@ target = D84DAD4C2B17428D003CF120 /* SentryTestUtilsDynamic */; targetProxy = D84DAD5B2B1742C1003CF120 /* PBXContainerItemProxy */; }; - D85153032CA2B5F60070F669 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D8199DA929376E9B0074249E /* SentrySwiftUI */; - targetProxy = D85153022CA2B5F60070F669 /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ diff --git a/Sources/Sentry/SentryHub.m b/Sources/Sentry/SentryHub.m index a83f66c71c0..0a2ac01c790 100644 --- a/Sources/Sentry/SentryHub.m +++ b/Sources/Sentry/SentryHub.m @@ -742,11 +742,7 @@ - (BOOL)eventContainsOnlyHandledErrors:(NSDictionary *)eventDictionary - (void)reportFullyDisplayed { #if SENTRY_HAS_UIKIT - if (_client.options.enableTimeToFullDisplayTracing) { - [SentryUIViewControllerPerformanceTracker.shared reportFullyDisplayed]; - } else { - SENTRY_LOG_DEBUG(@"The options `enableTimeToFullDisplay` is disabled."); - } + [SentryUIViewControllerPerformanceTracker.shared reportFullyDisplayed]; #endif // SENTRY_HAS_UIKIT } diff --git a/Sources/Sentry/SentryPerformanceTrackingIntegration.m b/Sources/Sentry/SentryPerformanceTrackingIntegration.m index 8f2eb8a2098..a8bd8868fde 100644 --- a/Sources/Sentry/SentryPerformanceTrackingIntegration.m +++ b/Sources/Sentry/SentryPerformanceTrackingIntegration.m @@ -46,7 +46,7 @@ - (BOOL)installWithOptions:(SentryOptions *)options binaryImageCache:[SentryDependencyContainer.sharedInstance binaryImageCache]]; [self.swizzling start]; - SentryUIViewControllerPerformanceTracker.shared.enableWaitForFullDisplay + SentryUIViewControllerPerformanceTracker.shared.alwaysWaitForFullDisplay = options.enableTimeToFullDisplayTracing; return YES; diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index 77c4e52c85a..177e0a2be3f 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -42,7 +42,7 @@ [SentryDependencyContainer.sharedInstance.dispatchQueueWrapper dispatchAsyncWithBlock:^{ BOOL shouldStopAndTransmitLaunchProfile = options.profilesSampleRate != nil; # if SENTRY_HAS_UIKIT - if (SentryUIViewControllerPerformanceTracker.shared.enableWaitForFullDisplay) { + if (SentryUIViewControllerPerformanceTracker.shared.alwaysWaitForFullDisplay) { shouldStopAndTransmitLaunchProfile = NO; } # endif // SENTRY_HAS_UIKIT diff --git a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m index a41d273c476..12414f435cc 100644 --- a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m +++ b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m @@ -45,7 +45,7 @@ - (instancetype)init self.inAppLogic = [[SentryInAppLogic alloc] initWithInAppIncludes:options.inAppIncludes inAppExcludes:options.inAppExcludes]; - _enableWaitForFullDisplay = NO; + _alwaysWaitForFullDisplay = NO; _dispatchQueueWrapper = SentryDependencyContainer.sharedInstance.dispatchQueueWrapper; } return self; @@ -167,7 +167,7 @@ - (void)startRootSpanFor:(UIViewController *)controller SentryTimeToDisplayTracker *ttdTracker = [[SentryTimeToDisplayTracker alloc] initWithName: [SwiftDescriptor getObjectClassName:controller] - waitForFullDisplay:self.enableWaitForFullDisplay + waitForFullDisplay:self.alwaysWaitForFullDisplay dispatchQueueWrapper:_dispatchQueueWrapper]; if ([ttdTracker startForTracer:(SentryTracer *)vcSpan]) { @@ -181,7 +181,16 @@ - (void)startRootSpanFor:(UIViewController *)controller - (void)reportFullyDisplayed { - [self.currentTTDTracker reportFullyDisplayed]; + SentryTimeToDisplayTracker* tracker = self.currentTTDTracker; + if (tracker) { + if (tracker.waitForFullDisplay) { + [self.currentTTDTracker reportFullyDisplayed]; + } else { + SENTRY_LOG_DEBUG(@"Transaction is not waiting for full display report. You can enable `enableTimeToFullDisplay` option, or use the waitForFullDisplay property in our `SentryTracedView` view for SwiftUI."); + } + } else { + SENTRY_LOG_DEBUG(@"No screen transaction being tracked right now.") + } } - (void)setTimeToDisplayTracker:(SentryTimeToDisplayTracker *)ttdTracker { diff --git a/Sources/Sentry/include/SentryUIViewControllerPerformanceTracker.h b/Sources/Sentry/include/SentryUIViewControllerPerformanceTracker.h index ef8f28b239a..0355832393f 100644 --- a/Sources/Sentry/include/SentryUIViewControllerPerformanceTracker.h +++ b/Sources/Sentry/include/SentryUIViewControllerPerformanceTracker.h @@ -31,7 +31,7 @@ static NSString *const SENTRY_UI_PERFORMANCE_TRACKER_TTD_TRACKER @property (nonatomic, strong) SentryInAppLogic *inAppLogic; -@property (nonatomic) BOOL enableWaitForFullDisplay; +@property (nonatomic) BOOL alwaysWaitForFullDisplay; /** * Measures @c controller's @c loadView method. diff --git a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h index 2d4538d8070..ae6a5279bdc 100644 --- a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h +++ b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h @@ -103,4 +103,9 @@ typedef NS_ENUM(NSUInteger, SentrySpanStatus); @end +@interface SentrySDK () +@property (nonatomic, nullable, readonly, class) SentryOptions *options; +@end + + NS_ASSUME_NONNULL_END diff --git a/Sources/SentrySwiftUI/SentryTracedView.swift b/Sources/SentrySwiftUI/SentryTracedView.swift index b74edd3e720..9e13ad02e17 100644 --- a/Sources/SentrySwiftUI/SentryTracedView.swift +++ b/Sources/SentrySwiftUI/SentryTracedView.swift @@ -1,50 +1,13 @@ #if canImport(SwiftUI) import Foundation +import SwiftUI + #if CARTHAGE || SWIFT_PACKAGE @_implementationOnly import SentryInternal import Sentry #endif -import SwiftUI -@available(iOS 13, macOS 10.15, tvOS 13, *) -struct SentryTrackView: UIViewRepresentable { - - let name : String - let waitForFullDisplay : Bool - - class SentryView: UIView { - - let name : String - let waitForFullDisplay : Bool - - init(name: String, waitForFullDisplay: Bool) { - self.name = name - self.waitForFullDisplay = waitForFullDisplay - super.init(frame: CGRect(origin: .zero, size: CGSize(width: 1, height: 1))) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func didMoveToWindow() { - super.didMoveToWindow() - - let ttdTracker = SentryTimeToDisplayTracker(name: "", waitForFullDisplay: true) - print(ttdTracker); - } - } - - func makeUIView(context: Context) -> UIView { - let view = SentryView(name: name, waitForFullDisplay: waitForFullDisplay) - view.isUserInteractionEnabled = false - return view - } - - func updateUIView(_ uiView: UIView, context: Context) { - } -} /// A control to measure the performance of your views and send the result as a transaction to Sentry.io. /// @@ -79,13 +42,21 @@ public struct SentryTracedView: View { let content: () -> Content let name: String let nameSource: SentryTransactionNameSource + let waitforFullDisplay: Bool let traceOrigin = "auto.ui.swift_ui" - public init(_ viewName: String? = nil, content: @escaping () -> Content) { + /// Creates a view that measures the performance of its `content`. + /// + /// - Parameter viewName: The name that will be used for the span, if nil we try to get the name of the content class. + /// - Parameter waitForFullDisplay: Indicates whether this view transaction should wait for `SentrySDK.reportFullyDisplayed()` + /// in case you need to track some asyncronous task. This is ignored for any `SentryTracedView` that is child of another `SentryTracedView` + /// - Parameter content: The content that you want to track the performance + public init(_ viewName: String? = nil, waitForFullDisplay: Bool? = nil, @ViewBuilder content: @escaping () -> Content) { self.content = content self.name = viewName ?? SentryTracedView.extractName(content: Content.self) self.nameSource = viewName == nil ? .component : .custom + self.waitforFullDisplay = waitForFullDisplay ?? SentrySDK.options?.enableTimeToFullDisplayTracing ?? false } private static func extractName(content: Any) -> String { @@ -99,25 +70,30 @@ public struct SentryTracedView: View { } public var body: some View { - let content = !viewAppeared ? content() : tracedContent() - return content.onAppear { - viewAppeared = true - } - } + var trace: SentryTracer? + var spanId: SpanId? - private func tracedContent() -> Content { - let transactionCreated = ensureTransactionExists() + if !viewAppeared { + trace = ensureTransactionExists() + spanId = createAndPushBodySpan(transactionCreated: trace != nil) + } - let spanId = createAndPushBodySpan(transactionCreated: transactionCreated) defer { - finishSpan(spanId) + if let spanId = spanId { + finishSpan(spanId) + } } + // We need to add a UIView to the view hierarchy to be able to + // monitor ui life cycles. We will use the background modifier + // to add this tracking view behind the content. return content() + .background(TracingView(name: self.name, waitForFullDisplay: self.waitforFullDisplay, tracer: trace)) + .onAppear { viewAppeared = true } } - private func ensureTransactionExists() -> Bool { - guard SentryPerformanceTracker.shared.activeSpanId() == nil else { return false } + private func ensureTransactionExists() -> SentryTracer? { + guard SentryPerformanceTracker.shared.activeSpanId() == nil else { return nil } let transactionId = SentryPerformanceTracker.shared.startSpan( withName: name, @@ -135,7 +111,7 @@ public struct SentryTracedView: View { self.finishSpan(transactionId) } - return true + return SentryPerformanceTracker.shared.getSpan(transactionId) as? SentryTracer } private func createAndPushBodySpan(transactionCreated: Bool) -> SpanId { diff --git a/Sources/SentrySwiftUI/TracingView.swift b/Sources/SentrySwiftUI/TracingView.swift new file mode 100644 index 00000000000..0f7cd1cec59 --- /dev/null +++ b/Sources/SentrySwiftUI/TracingView.swift @@ -0,0 +1,59 @@ +#if canImport(SwiftUI) + +import Foundation +import SwiftUI + +#if CARTHAGE || SWIFT_PACKAGE +@_implementationOnly import SentryInternal +#endif + + +@available(iOS 13, macOS 10.15, tvOS 13, *) +struct TracingView: UIViewRepresentable { + + let name: String + let waitForFullDisplay: Bool + let tracer: SentryTracer? + + class SentryTracingView: UIView { + + let tracer: SentryTracer? + let tracker: SentryTimeToDisplayTracker? + + init(name: String, waitForFullDisplay: Bool, tracer: SentryTracer?) { + self.tracer = tracer + if let tracer = self.tracer { + let tracker = SentryTimeToDisplayTracker(name: name, waitForFullDisplay: waitForFullDisplay) + self.tracker = tracker + SentryUIViewControllerPerformanceTracker.shared.setTimeToDisplay(tracker) + tracker.start(for: tracer) + } else { + tracker = nil + } + + super.init(frame: CGRect(origin: .zero, size: CGSize(width: 1, height: 1))) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func didMoveToWindow() { + super.didMoveToWindow() + // Reports initial display when this view is added to the view hierarchy + // This is the equivalent of viewDidAppear of a UIViewController + tracker?.reportInitialDisplay() + } + } + + func makeUIView(context: Context) -> UIView { + let view = SentryTracingView(name: name, waitForFullDisplay: waitForFullDisplay, tracer: tracer) + view.isUserInteractionEnabled = false + return view + } + + func updateUIView(_ uiView: UIView, context: Context) { + //Intentionally left blank. Nothing to update here. + } +} +#endif diff --git a/Tests/SentryProfilerTests/SentryAppLaunchProfilingTests.swift b/Tests/SentryProfilerTests/SentryAppLaunchProfilingTests.swift index 8e165de7cd9..f3ff29ec503 100644 --- a/Tests/SentryProfilerTests/SentryAppLaunchProfilingTests.swift +++ b/Tests/SentryProfilerTests/SentryAppLaunchProfilingTests.swift @@ -36,7 +36,7 @@ final class SentryAppLaunchProfilingSwiftTests: XCTestCase { let appStartMeasurement = fixture.getAppStartMeasurement(type: .cold) SentrySDK.setAppStartMeasurement(appStartMeasurement) let tracer = try fixture.newTransaction(testingAppLaunchSpans: true, automaticTransaction: true) - let ttd = SentryTimeToDisplayTracker(for: UIViewController(nibName: nil, bundle: nil), waitForFullDisplay: true, dispatchQueueWrapper: fixture.dispatchQueueWrapper) + let ttd = SentryTimeToDisplayTracker(name: "UIViewController", waitForFullDisplay: true, dispatchQueueWrapper: fixture.dispatchQueueWrapper) ttd.start(for: tracer) ttd.reportInitialDisplay() ttd.reportFullyDisplayed() @@ -55,7 +55,7 @@ final class SentryAppLaunchProfilingSwiftTests: XCTestCase { XCTAssert(try XCTUnwrap(SentryTraceProfiler.getCurrentProfiler()).isRunning()) SentrySDK.setStart(fixture.options) - let ttd = SentryTimeToDisplayTracker(for: UIViewController(nibName: nil, bundle: nil), waitForFullDisplay: true, dispatchQueueWrapper: fixture.dispatchQueueWrapper) + let ttd = SentryTimeToDisplayTracker(name: "UIViewController", waitForFullDisplay: true, dispatchQueueWrapper: fixture.dispatchQueueWrapper) ttd.start(for: try XCTUnwrap(sentry_launchTracer)) ttd.reportInitialDisplay() ttd.reportFullyDisplayed() @@ -76,7 +76,7 @@ final class SentryAppLaunchProfilingSwiftTests: XCTestCase { let appStartMeasurement = fixture.getAppStartMeasurement(type: .cold) SentrySDK.setAppStartMeasurement(appStartMeasurement) let tracer = try fixture.newTransaction(testingAppLaunchSpans: true, automaticTransaction: true) - let ttd = SentryTimeToDisplayTracker(for: UIViewController(nibName: nil, bundle: nil), waitForFullDisplay: false, dispatchQueueWrapper: fixture.dispatchQueueWrapper) + let ttd = SentryTimeToDisplayTracker(name: "UIViewController", waitForFullDisplay: false, dispatchQueueWrapper: fixture.dispatchQueueWrapper) ttd.start(for: tracer) ttd.reportInitialDisplay() fixture.displayLinkWrapper.call() @@ -94,7 +94,7 @@ final class SentryAppLaunchProfilingSwiftTests: XCTestCase { XCTAssert(try XCTUnwrap(SentryTraceProfiler.getCurrentProfiler()).isRunning()) SentrySDK.setStart(fixture.options) - let ttd = SentryTimeToDisplayTracker(for: UIViewController(nibName: nil, bundle: nil), waitForFullDisplay: false, dispatchQueueWrapper: fixture.dispatchQueueWrapper) + let ttd = SentryTimeToDisplayTracker(name: "UIViewController", waitForFullDisplay: false, dispatchQueueWrapper: fixture.dispatchQueueWrapper) ttd.start(for: try XCTUnwrap(sentry_launchTracer)) ttd.reportInitialDisplay() fixture.displayLinkWrapper.call() diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift index 077bf7479f4..e03c32085bf 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift @@ -22,7 +22,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { } func getSut(for controller: UIViewController, waitForFullDisplay: Bool) -> SentryTimeToDisplayTracker { - return SentryTimeToDisplayTracker(for: controller, waitForFullDisplay: waitForFullDisplay, dispatchQueueWrapper: SentryDispatchQueueWrapper()) + return SentryTimeToDisplayTracker(name: String(describing: controller), waitForFullDisplay: waitForFullDisplay, dispatchQueueWrapper: SentryDispatchQueueWrapper()) } func getTracer() throws -> SentryTracer { @@ -305,7 +305,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { func testReportFullyDisplayed_GetsDispatchedOnMainQueue() { let dispatchQueueWrapper = TestSentryDispatchQueueWrapper() - let sut = SentryTimeToDisplayTracker(for: UIViewController(), waitForFullDisplay: true, dispatchQueueWrapper: dispatchQueueWrapper) + let sut = SentryTimeToDisplayTracker(name: "UIViewController", waitForFullDisplay: true, dispatchQueueWrapper: dispatchQueueWrapper) let invocationsBefore = dispatchQueueWrapper.blockOnMainInvocations.count sut.reportFullyDisplayed() diff --git a/Tests/SentryTests/SentryHubTests.swift b/Tests/SentryTests/SentryHubTests.swift index 30a21d12f48..1643f77a099 100644 --- a/Tests/SentryTests/SentryHubTests.swift +++ b/Tests/SentryTests/SentryHubTests.swift @@ -893,24 +893,21 @@ class SentryHubTests: XCTestCase { #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) func test_reportFullyDisplayed_enableTimeToFullDisplay_YES() { - fixture.options.enableTimeToFullDisplayTracing = true let sut = fixture.getSut(fixture.options) - let testTTDTracker = TestTimeToDisplayTracker() + let testTTDTracker = TestTimeToDisplayTracker(waitForFullDisplay: true) Dynamic(SentryUIViewControllerPerformanceTracker.shared).currentTTDTracker = testTTDTracker sut.reportFullyDisplayed() XCTAssertTrue(testTTDTracker.registerFullDisplayCalled) - } func test_reportFullyDisplayed_enableTimeToFullDisplay_NO() { - fixture.options.enableTimeToFullDisplayTracing = false let sut = fixture.getSut(fixture.options) - let testTTDTracker = TestTimeToDisplayTracker() + let testTTDTracker = TestTimeToDisplayTracker(waitForFullDisplay: false) Dynamic(SentryUIViewControllerPerformanceTracker.shared).currentTTDTracker = testTTDTracker @@ -1189,8 +1186,8 @@ class SentryHubTests: XCTestCase { #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) class TestTimeToDisplayTracker: SentryTimeToDisplayTracker { - init() { - super.init(for: UIViewController(), waitForFullDisplay: false, dispatchQueueWrapper: SentryDispatchQueueWrapper()) + init(waitForFullDisplay: Bool = false) { + super.init(name: "UIViewController", waitForFullDisplay: waitForFullDisplay, dispatchQueueWrapper: SentryDispatchQueueWrapper()) } var registerFullDisplayCalled = false From e024c6ca6244d15ed4def40ba59e8a7040a7c197 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 4 Dec 2024 12:43:26 +0100 Subject: [PATCH 03/12] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e6b4a83bad..6531d5acf29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- SwiftUI time for initial display and time for full display () + ## 8.42.0-beta.1 ### Features From 671956fa14ae2ceedd19a7325a2b3dd2b10754a5 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 4 Dec 2024 11:45:57 +0000 Subject: [PATCH 04/12] Format code --- .../iOS-SwiftUI/iOS-SwiftUI/ContentView.swift | 2 +- Sources/Sentry/SentryTimeToDisplayTracker.m | 17 +++++++++-------- .../SentryUIViewControllerPerformanceTracker.m | 17 ++++++++++------- .../Sentry/include/SentryTimeToDisplayTracker.h | 3 +-- .../SentryInternal/SentryInternal.h | 13 +++++-------- Sources/SentrySwiftUI/SentryTracedView.swift | 3 +-- Sources/SentrySwiftUI/TracingView.swift | 1 - 7 files changed, 27 insertions(+), 29 deletions(-) diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift b/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift index 07608490656..6262d3fc452 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift @@ -213,7 +213,7 @@ struct ContentView: View { } SecondView() } - } + } } } } diff --git a/Sources/Sentry/SentryTimeToDisplayTracker.m b/Sources/Sentry/SentryTimeToDisplayTracker.m index 8703d482949..29ccd541fad 100644 --- a/Sources/Sentry/SentryTimeToDisplayTracker.m +++ b/Sources/Sentry/SentryTimeToDisplayTracker.m @@ -53,9 +53,11 @@ - (instancetype)initWithName:(NSString *)name return self; } -- (instancetype)initWithName:(NSString *)name - waitForFullDisplay:(BOOL)waitForFullDisplay { - return [self initWithName:name waitForFullDisplay:waitForFullDisplay dispatchQueueWrapper:SentryDependencyContainer.sharedInstance.dispatchQueueWrapper]; +- (instancetype)initWithName:(NSString *)name waitForFullDisplay:(BOOL)waitForFullDisplay +{ + return [self initWithName:name + waitForFullDisplay:waitForFullDisplay + dispatchQueueWrapper:SentryDependencyContainer.sharedInstance.dispatchQueueWrapper]; } - (BOOL)startForTracer:(SentryTracer *)tracer @@ -67,17 +69,16 @@ - (BOOL)startForTracer:(SentryTracer *)tracer } SENTRY_LOG_DEBUG(@"Starting initial display span"); - self.initialDisplaySpan = [tracer - startChildWithOperation:SentrySpanOperationUILoadInitialDisplay - description:[NSString stringWithFormat:@"%@ initial display", _name]]; + self.initialDisplaySpan = + [tracer startChildWithOperation:SentrySpanOperationUILoadInitialDisplay + description:[NSString stringWithFormat:@"%@ initial display", _name]]; self.initialDisplaySpan.origin = SentryTraceOriginAutoUITimeToDisplay; if (self.waitForFullDisplay) { SENTRY_LOG_DEBUG(@"Starting full display span"); self.fullDisplaySpan = [tracer startChildWithOperation:SentrySpanOperationUILoadFullDisplay - description:[NSString stringWithFormat:@"%@ full display", - _name]]; + description:[NSString stringWithFormat:@"%@ full display", _name]]; self.fullDisplaySpan.origin = SentryTraceOriginManualUITimeToDisplay; // By concept TTID and TTFD spans should have the same beginning, diff --git a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m index 12414f435cc..f2ed5351b87 100644 --- a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m +++ b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m @@ -165,10 +165,10 @@ - (void)startRootSpanFor:(UIViewController *)controller [self.currentTTDTracker finishSpansIfNotFinished]; - SentryTimeToDisplayTracker *ttdTracker = - [[SentryTimeToDisplayTracker alloc] initWithName: [SwiftDescriptor getObjectClassName:controller] - waitForFullDisplay:self.alwaysWaitForFullDisplay - dispatchQueueWrapper:_dispatchQueueWrapper]; + SentryTimeToDisplayTracker *ttdTracker = [[SentryTimeToDisplayTracker alloc] + initWithName:[SwiftDescriptor getObjectClassName:controller] + waitForFullDisplay:self.alwaysWaitForFullDisplay + dispatchQueueWrapper:_dispatchQueueWrapper]; if ([ttdTracker startForTracer:(SentryTracer *)vcSpan]) { objc_setAssociatedObject(controller, &SENTRY_UI_PERFORMANCE_TRACKER_TTD_TRACKER, ttdTracker, @@ -181,19 +181,22 @@ - (void)startRootSpanFor:(UIViewController *)controller - (void)reportFullyDisplayed { - SentryTimeToDisplayTracker* tracker = self.currentTTDTracker; + SentryTimeToDisplayTracker *tracker = self.currentTTDTracker; if (tracker) { if (tracker.waitForFullDisplay) { [self.currentTTDTracker reportFullyDisplayed]; } else { - SENTRY_LOG_DEBUG(@"Transaction is not waiting for full display report. You can enable `enableTimeToFullDisplay` option, or use the waitForFullDisplay property in our `SentryTracedView` view for SwiftUI."); + SENTRY_LOG_DEBUG(@"Transaction is not waiting for full display report. You can enable " + @"`enableTimeToFullDisplay` option, or use the waitForFullDisplay " + @"property in our `SentryTracedView` view for SwiftUI."); } } else { SENTRY_LOG_DEBUG(@"No screen transaction being tracked right now.") } } -- (void)setTimeToDisplayTracker:(SentryTimeToDisplayTracker *)ttdTracker { +- (void)setTimeToDisplayTracker:(SentryTimeToDisplayTracker *)ttdTracker +{ self.currentTTDTracker = ttdTracker; } diff --git a/Sources/Sentry/include/SentryTimeToDisplayTracker.h b/Sources/Sentry/include/SentryTimeToDisplayTracker.h index a5d39d90bb1..306ff1dcc9c 100644 --- a/Sources/Sentry/include/SentryTimeToDisplayTracker.h +++ b/Sources/Sentry/include/SentryTimeToDisplayTracker.h @@ -29,8 +29,7 @@ SENTRY_NO_INIT waitForFullDisplay:(BOOL)waitForFullDisplay dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; -- (instancetype)initWithName:(NSString *)name - waitForFullDisplay:(BOOL)waitForFullDisplay; +- (instancetype)initWithName:(NSString *)name waitForFullDisplay:(BOOL)waitForFullDisplay; - (BOOL)startForTracer:(SentryTracer *)tracer; diff --git a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h index ae6a5279bdc..2c33ef72f4a 100644 --- a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h +++ b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h @@ -8,9 +8,9 @@ #import #if __has_include() -#import +# import #elif __has_include("Sentry.h") -#import "Sentry.h" +# import "Sentry.h" #endif NS_ASSUME_NONNULL_BEGIN @@ -23,7 +23,6 @@ typedef NS_ENUM(NSInteger, SentryTransactionNameSource); @interface SentryTracer : NSObject @end - typedef NS_ENUM(NSUInteger, SentrySpanStatus); @interface SentryPerformanceTracker : NSObject @@ -67,8 +66,8 @@ typedef NS_ENUM(NSUInteger, SentrySpanStatus); @end @interface SentryTimeToDisplayTracker : NSObject --(instancetype)init NS_UNAVAILABLE; -+(instancetype) new NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; @property (nullable, nonatomic, weak, readonly) SentrySpan *initialDisplaySpan; @@ -80,8 +79,7 @@ typedef NS_ENUM(NSUInteger, SentrySpanStatus); waitForFullDisplay:(BOOL)waitForFullDisplay dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; -- (instancetype)initWithName:(NSString *)name - waitForFullDisplay:(BOOL)waitForFullDisplay; +- (instancetype)initWithName:(NSString *)name waitForFullDisplay:(BOOL)waitForFullDisplay; - (BOOL)startForTracer:(SentryTracer *)tracer; @@ -107,5 +105,4 @@ typedef NS_ENUM(NSUInteger, SentrySpanStatus); @property (nonatomic, nullable, readonly, class) SentryOptions *options; @end - NS_ASSUME_NONNULL_END diff --git a/Sources/SentrySwiftUI/SentryTracedView.swift b/Sources/SentrySwiftUI/SentryTracedView.swift index 9e13ad02e17..883d9ab23f7 100644 --- a/Sources/SentrySwiftUI/SentryTracedView.swift +++ b/Sources/SentrySwiftUI/SentryTracedView.swift @@ -4,11 +4,10 @@ import Foundation import SwiftUI #if CARTHAGE || SWIFT_PACKAGE -@_implementationOnly import SentryInternal import Sentry +@_implementationOnly import SentryInternal #endif - /// A control to measure the performance of your views and send the result as a transaction to Sentry.io. /// /// You create a transaction by wrapping your views with this. diff --git a/Sources/SentrySwiftUI/TracingView.swift b/Sources/SentrySwiftUI/TracingView.swift index 0f7cd1cec59..72c29d9438d 100644 --- a/Sources/SentrySwiftUI/TracingView.swift +++ b/Sources/SentrySwiftUI/TracingView.swift @@ -7,7 +7,6 @@ import SwiftUI @_implementationOnly import SentryInternal #endif - @available(iOS 13, macOS 10.15, tvOS 13, *) struct TracingView: UIViewRepresentable { From 5c531b6f2cd164cdf0105d880e8cbaa46abf0e68 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 4 Dec 2024 12:46:10 +0100 Subject: [PATCH 05/12] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6531d5acf29..092efee3c2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- SwiftUI time for initial display and time for full display () +- SwiftUI time for initial display and time for full display (#4596) ## 8.42.0-beta.1 From e6c6f18bbb58d1796e5d3196d86a9ece419a499e Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 4 Dec 2024 13:15:11 +0100 Subject: [PATCH 06/12] fix for macOS --- .../SentryInternal/SentryInternal.h | 1 + Sources/SentrySwiftUI/SentryTracedView.swift | 27 ++++++++++++++----- Sources/SentrySwiftUI/TracingView.swift | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h index 2c33ef72f4a..8852d961d0d 100644 --- a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h +++ b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h @@ -19,6 +19,7 @@ typedef NS_ENUM(NSInteger, SentryTransactionNameSource); @class SentrySpanId; @class SentrySpan; +@class SentryDispatchQueueWrapper; @interface SentryTracer : NSObject @end diff --git a/Sources/SentrySwiftUI/SentryTracedView.swift b/Sources/SentrySwiftUI/SentryTracedView.swift index 883d9ab23f7..2e9637b4f4a 100644 --- a/Sources/SentrySwiftUI/SentryTracedView.swift +++ b/Sources/SentrySwiftUI/SentryTracedView.swift @@ -41,10 +41,11 @@ public struct SentryTracedView: View { let content: () -> Content let name: String let nameSource: SentryTransactionNameSource - let waitforFullDisplay: Bool - let traceOrigin = "auto.ui.swift_ui" +#if canImport(SwiftUI) && canImport(UIKit) && os(iOS) || os(tvOS) + let waitforFullDisplay: Bool + /// Creates a view that measures the performance of its `content`. /// /// - Parameter viewName: The name that will be used for the span, if nil we try to get the name of the content class. @@ -57,6 +58,17 @@ public struct SentryTracedView: View { self.nameSource = viewName == nil ? .component : .custom self.waitforFullDisplay = waitForFullDisplay ?? SentrySDK.options?.enableTimeToFullDisplayTracing ?? false } +#else + /// Creates a view that measures the performance of its `content`. + /// + /// - Parameter viewName: The name that will be used for the span, if nil we try to get the name of the content class. + /// - Parameter content: The content that you want to track the performance + public init(_ viewName: String? = nil, @ViewBuilder content: @escaping () -> Content) { + self.content = content + self.name = viewName ?? SentryTracedView.extractName(content: Content.self) + self.nameSource = viewName == nil ? .component : .custom + } +#endif private static func extractName(content: Any) -> String { var result = String(describing: content) @@ -83,12 +95,15 @@ public struct SentryTracedView: View { } } - // We need to add a UIView to the view hierarchy to be able to - // monitor ui life cycles. We will use the background modifier - // to add this tracking view behind the content. return content() - .background(TracingView(name: self.name, waitForFullDisplay: self.waitforFullDisplay, tracer: trace)) .onAppear { viewAppeared = true } +#if canImport(SwiftUI) && canImport(UIKit) && os(iOS) || os(tvOS) + // We need to add a UIView to the view hierarchy to be able to + // monitor ui life cycles. We will use the background modifier + // to add this tracking view behind the content. + .background(TracingView(name: self.name, waitForFullDisplay: self.waitforFullDisplay, tracer: trace)) +#endif + } private func ensureTransactionExists() -> SentryTracer? { diff --git a/Sources/SentrySwiftUI/TracingView.swift b/Sources/SentrySwiftUI/TracingView.swift index 72c29d9438d..cb10d0c9c02 100644 --- a/Sources/SentrySwiftUI/TracingView.swift +++ b/Sources/SentrySwiftUI/TracingView.swift @@ -1,4 +1,4 @@ -#if canImport(SwiftUI) +#if canImport(SwiftUI) && canImport(UIKit) && os(iOS) || os(tvOS) import Foundation import SwiftUI From d6884c501162230d03a2bc327292fe555e5d617e Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 4 Dec 2024 13:36:38 +0100 Subject: [PATCH 07/12] fix tests --- SentryTestUtils/ClearTestState.swift | 2 +- ...SentryPerformanceTrackingIntegrationTests.swift | 4 ++-- ...ryUIViewControllerPerformanceTrackerTests.swift | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/SentryTestUtils/ClearTestState.swift b/SentryTestUtils/ClearTestState.swift index 36ec3b75773..485fa70054e 100644 --- a/SentryTestUtils/ClearTestState.swift +++ b/SentryTestUtils/ClearTestState.swift @@ -37,7 +37,7 @@ class TestCleanup: NSObject { setenv("ActivePrewarm", "0", 1) SentryAppStartTracker.load() - SentryUIViewControllerPerformanceTracker.shared.enableWaitForFullDisplay = false + SentryUIViewControllerPerformanceTracker.shared.alwaysWaitForFullDisplay = false SentryDependencyContainer.sharedInstance().swizzleWrapper.removeAllCallbacks() SentryDependencyContainer.sharedInstance().fileManager.clearDiskState() diff --git a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackingIntegrationTests.swift b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackingIntegrationTests.swift index 37376eabe95..35144a884f7 100644 --- a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackingIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackingIntegrationTests.swift @@ -74,7 +74,7 @@ class SentryPerformanceTrackingIntegrationTests: XCTestCase { options.enableTimeToFullDisplayTracing = true sut.install(with: options) - XCTAssertTrue(SentryUIViewControllerPerformanceTracker.shared.enableWaitForFullDisplay) + XCTAssertTrue(SentryUIViewControllerPerformanceTracker.shared.alwaysWaitForFullDisplay) } func testConfigure_dontWaitForDisplay() { @@ -85,7 +85,7 @@ class SentryPerformanceTrackingIntegrationTests: XCTestCase { options.enableTimeToFullDisplayTracing = false sut.install(with: options) - XCTAssertFalse(SentryUIViewControllerPerformanceTracker.shared.enableWaitForFullDisplay) + XCTAssertFalse(SentryUIViewControllerPerformanceTracker.shared.alwaysWaitForFullDisplay) } #endif diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift index 0358748c697..a27e04fe51e 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift @@ -356,7 +356,7 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { func testReportFullyDisplayed() throws { let sut = fixture.getSut() - sut.enableWaitForFullDisplay = true + sut.alwaysWaitForFullDisplay = true let viewController = fixture.viewController let tracker = fixture.tracker var tracer: SentryTracer? @@ -661,7 +661,7 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { var tracer: SentryTracer? - sut.enableWaitForFullDisplay = true + sut.alwaysWaitForFullDisplay = true sut.viewControllerLoadView(firstController) { tracer = self.getStack(tracker).first as? SentryTracer @@ -678,7 +678,7 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { var tracer: SentryTracer? - sut.enableWaitForFullDisplay = false + sut.alwaysWaitForFullDisplay = false sut.viewControllerLoadView(firstController) { tracer = self.getStack(tracker).first as? SentryTracer @@ -711,7 +711,7 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { func test_OnlyViewDidLoadTTFDEnabled_CreatesTTIDAndTTFDSpans() throws { let sut = fixture.getSut() - sut.enableWaitForFullDisplay = true + sut.alwaysWaitForFullDisplay = true let tracker = fixture.tracker var tracer: SentryTracer! @@ -776,7 +776,7 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { var firstTracer: SentryTracer? var secondTracer: SentryTracer? - sut.enableWaitForFullDisplay = true + sut.alwaysWaitForFullDisplay = true let expectedFirstTTFDStartTimestamp = fixture.dateProvider.date() @@ -823,7 +823,7 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { var firstTracer: SentryTracer? var secondTracer: SentryTracer? - sut.enableWaitForFullDisplay = true + sut.alwaysWaitForFullDisplay = true sut.viewControllerLoadView(firstController) { firstTracer = self.getStack(tracker).first as? SentryTracer @@ -863,7 +863,7 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { var firstTracer: SentryTracer? var secondTracer: SentryTracer? - sut.enableWaitForFullDisplay = true + sut.alwaysWaitForFullDisplay = true let expectedFirstTTFDStartTimestamp = fixture.dateProvider.date() sut.viewControllerLoadView(firstController) { From ebdbc79ee118724ecd623b0046fb00cc2732a745 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 4 Dec 2024 15:04:32 +0100 Subject: [PATCH 08/12] Update SentryTracedView.swift --- Sources/SentrySwiftUI/SentryTracedView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SentrySwiftUI/SentryTracedView.swift b/Sources/SentrySwiftUI/SentryTracedView.swift index 2e9637b4f4a..319b11fcfb1 100644 --- a/Sources/SentrySwiftUI/SentryTracedView.swift +++ b/Sources/SentrySwiftUI/SentryTracedView.swift @@ -2,9 +2,9 @@ import Foundation import SwiftUI +import Sentry #if CARTHAGE || SWIFT_PACKAGE -import Sentry @_implementationOnly import SentryInternal #endif From 78e6b6641294d5049f128c705590a4ad2b71a397 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 4 Dec 2024 14:05:38 +0000 Subject: [PATCH 09/12] Format code --- Sources/SentrySwiftUI/SentryTracedView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SentrySwiftUI/SentryTracedView.swift b/Sources/SentrySwiftUI/SentryTracedView.swift index 319b11fcfb1..590d68e0160 100644 --- a/Sources/SentrySwiftUI/SentryTracedView.swift +++ b/Sources/SentrySwiftUI/SentryTracedView.swift @@ -1,8 +1,8 @@ #if canImport(SwiftUI) import Foundation -import SwiftUI import Sentry +import SwiftUI #if CARTHAGE || SWIFT_PACKAGE @_implementationOnly import SentryInternal From 04c6fdd39bb43bd23330597e3492d7c22dc2e7af Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 4 Dec 2024 16:18:07 +0100 Subject: [PATCH 10/12] Update SentryTimeToDisplayTrackerTest.swift --- .../SentryTimeToDisplayTrackerTest.swift | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift index e03c32085bf..e9d2ebe7781 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift @@ -21,8 +21,8 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { framesTracker.start() } - func getSut(for controller: UIViewController, waitForFullDisplay: Bool) -> SentryTimeToDisplayTracker { - return SentryTimeToDisplayTracker(name: String(describing: controller), waitForFullDisplay: waitForFullDisplay, dispatchQueueWrapper: SentryDispatchQueueWrapper()) + func getSut(name: String, waitForFullDisplay: Bool) -> SentryTimeToDisplayTracker { + return SentryTimeToDisplayTracker(name:name, waitForFullDisplay: waitForFullDisplay, dispatchQueueWrapper: SentryDispatchQueueWrapper()) } func getTracer() throws -> SentryTracer { @@ -52,7 +52,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { func testNoSpansCreated_WhenFramesTrackerNotRunning() throws { fixture.framesTracker.stop() - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: false) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: false) fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 7)) let tracer = try fixture.getTracer() @@ -67,7 +67,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { } func testReportInitialDisplay_notWaitingForFullDisplay() throws { - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: false) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: false) fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 7)) let tracer = try fixture.getTracer() @@ -101,7 +101,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { func testReportInitialDisplay_waitForFullDisplay() throws { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 7)) - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: true) let tracer = try fixture.getTracer() sut.start(for: tracer) @@ -130,7 +130,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { } func testReportFullDisplay_notWaitingForFullDisplay() throws { - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: false) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: false) let tracer = try fixture.getTracer() sut.start(for: tracer) @@ -150,7 +150,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { func testReportFullDisplay_waitingForFullDisplay() throws { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9)) - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: true) let tracer = try fixture.getTracer() sut.start(for: tracer) @@ -184,7 +184,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { func testWaitingForFullDisplay_ReportFullDisplayBeforeInitialDisplay() throws { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9)) - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: true) let tracer = try fixture.getTracer() sut.start(for: tracer) @@ -224,7 +224,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { } func testTracerFinishesBeforeReportInitialDisplay_FinishesInitialDisplaySpan() throws { - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: false) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: false) fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 7)) let tracer = try fixture.getTracer() @@ -254,7 +254,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9)) fixture.dateProvider.driftTimeForEveryRead = true - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: true) let tracer = try fixture.getTracer() sut.start(for: tracer) @@ -269,7 +269,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { let tracer = try fixture.getTracer() - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: true) sut.start(for: tracer) @@ -319,7 +319,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { let tracer = try fixture.getTracer() - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: false) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: false) sut.start(for: tracer) @@ -351,7 +351,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 7)) - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: false) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: false) let tracer = try fixture.getTracer() sut.start(for: tracer) @@ -384,7 +384,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 7)) - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: true) let tracer = try fixture.getTracer() sut.start(for: tracer) @@ -416,7 +416,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { func testFinish_WithoutCallingReportFullyDisplayed() throws { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9)) - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: true) let tracer = try fixture.getTracer() sut.start(for: tracer) @@ -451,7 +451,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { func testFinish_WithoutTTID() throws { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9)) - let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) + let sut = fixture.getSut(name: "UIViewController", waitForFullDisplay: true) let tracer = try fixture.getTracer() sut.start(for: tracer) From 818aa34d91c8077a6a81bb506b0686734a1ec8b6 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 4 Dec 2024 15:19:15 +0000 Subject: [PATCH 11/12] Format code --- .../UIViewController/SentryTimeToDisplayTrackerTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift index e9d2ebe7781..865e5e45599 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift @@ -22,7 +22,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { } func getSut(name: String, waitForFullDisplay: Bool) -> SentryTimeToDisplayTracker { - return SentryTimeToDisplayTracker(name:name, waitForFullDisplay: waitForFullDisplay, dispatchQueueWrapper: SentryDispatchQueueWrapper()) + return SentryTimeToDisplayTracker(name: name, waitForFullDisplay: waitForFullDisplay, dispatchQueueWrapper: SentryDispatchQueueWrapper()) } func getTracer() throws -> SentryTracer { From 6bbebc7387753132268c9632ddb921818ec1729e Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 4 Dec 2024 16:30:00 +0100 Subject: [PATCH 12/12] Update SentrySDKTests.swift --- Tests/SentryTests/SentrySDKTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/SentryTests/SentrySDKTests.swift b/Tests/SentryTests/SentrySDKTests.swift index 75239a90d05..245a4f9f18e 100644 --- a/Tests/SentryTests/SentrySDKTests.swift +++ b/Tests/SentryTests/SentrySDKTests.swift @@ -688,8 +688,8 @@ class SentrySDKTests: XCTestCase { SentrySDK.start(options: fixture.options) - let testTTDTracker = TestTimeToDisplayTracker() - + let testTTDTracker = TestTimeToDisplayTracker(waitForFullDisplay: true) + Dynamic(SentryUIViewControllerPerformanceTracker.shared).currentTTDTracker = testTTDTracker SentrySDK.reportFullyDisplayed()