diff --git a/Core/Pixel.swift b/Core/Pixel.swift index be9771b5d2..c9d4e38470 100644 --- a/Core/Pixel.swift +++ b/Core/Pixel.swift @@ -159,6 +159,8 @@ public struct PixelParameters { // Persistent pixel public static let originalPixelTimestamp = "originalPixelTimestamp" public static let retriedPixel = "retriedPixel" + + public static let time = "time" } public struct PixelValues { diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 28b764a87f..b9765d989f 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -870,6 +870,12 @@ extension Pixel { case duckPlayerYouTubeOverlayNavigationOutsideYoutube case duckPlayerYouTubeOverlayNavigationClosed case duckPlayerYouTubeNavigationIdle30 + + // MARK: Launch time + case appDidFinishLaunchingTime(time: BucketAggregation) + case appDidShowUITime(time: BucketAggregation) + case appDidBecomeActiveTime(time: BucketAggregation) + } } @@ -1734,6 +1740,11 @@ extension Pixel.Event { case .duckPlayerYouTubeOverlayNavigationClosed: return "duckplayer.youtube.overlay.navigation.closed" case .duckPlayerYouTubeNavigationIdle30: return "duckplayer.youtube.overlay.idle-30" + // MARK: Launch time + case .appDidFinishLaunchingTime(let time): return "m_debug_app-did-finish-launching-time-\(time)" + case .appDidShowUITime(let time): return "m_debug_app-did-show-ui-time-\(time)" + case .appDidBecomeActiveTime(let time): return "m_debug_app-did-become-active-time-\(time)" + } } } diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 4051d58ea1..b8f8b9ec25 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -114,6 +114,9 @@ import os.log private let voiceSearchHelper = VoiceSearchHelper() private let marketplaceAdPostbackManager = MarketplaceAdPostbackManager() + + private var didFinishLaunchingStartTime: CFAbsoluteTime? + override init() { super.init() @@ -125,8 +128,19 @@ import os.log } // swiftlint:disable:next function_body_length + // swiftlint:disable:next cyclomatic_complexity func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + didFinishLaunchingStartTime = CFAbsoluteTimeGetCurrent() + defer { + if let didFinishLaunchingStartTime { + let launchTime = CFAbsoluteTimeGetCurrent() - didFinishLaunchingStartTime + Pixel.fire(pixel: .appDidFinishLaunchingTime(time: Pixel.Event.BucketAggregation(number: launchTime)), + withAdditionalParameters: [PixelParameters.time: String(launchTime)]) + } + } + + #if targetEnvironment(simulator) if ProcessInfo.processInfo.environment["UITESTING"] == "true" { // Disable hardware keyboards. @@ -358,7 +372,8 @@ import os.log voiceSearchHelper: voiceSearchHelper, featureFlagger: AppDependencyProvider.shared.featureFlagger, subscriptionCookieManager: subscriptionCookieManager, - textZoomCoordinator: makeTextZoomCoordinator()) + textZoomCoordinator: makeTextZoomCoordinator(), + appDidFinishLaunchingStartTime: didFinishLaunchingStartTime) main.loadViewIfNeeded() syncErrorHandler.alertPresenter = main @@ -560,6 +575,14 @@ import os.log func applicationDidBecomeActive(_ application: UIApplication) { guard !testing else { return } + defer { + if let didFinishLaunchingStartTime { + let launchTime = CFAbsoluteTimeGetCurrent() - didFinishLaunchingStartTime + Pixel.fire(pixel: .appDidBecomeActiveTime(time: Pixel.Event.BucketAggregation(number: launchTime)), + withAdditionalParameters: [PixelParameters.time: String(launchTime)]) + } + } + StorageInconsistencyMonitor().didBecomeActive(isProtectedDataAvailable: application.isProtectedDataAvailable) syncService.initializeIfNeeded() syncDataProviders.setUpDatabaseCleanersIfNeeded(syncService: syncService) @@ -754,6 +777,12 @@ import os.log suspendSync() syncDataProviders.bookmarksAdapter.cancelFaviconsFetching(application) privacyProDataReporter.saveApplicationLastSessionEnded() + resetAppStartTime() + } + + private func resetAppStartTime() { + didFinishLaunchingStartTime = nil + mainViewController?.appDidFinishLaunchingStartTime = nil } private func suspendSync() { diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 8a51278c6f..2105b1c6c5 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -183,6 +183,8 @@ class MainViewController: UIViewController { var historyManager: HistoryManaging var viewCoordinator: MainViewCoordinator! + var appDidFinishLaunchingStartTime: CFAbsoluteTime? + init( bookmarksDatabase: CoreDataDatabase, bookmarksDatabaseCleaner: BookmarkDatabaseCleaner, @@ -206,7 +208,8 @@ class MainViewController: UIViewController { featureFlagger: FeatureFlagger, fireproofing: Fireproofing = UserDefaultsFireproofing.shared, subscriptionCookieManager: SubscriptionCookieManaging, - textZoomCoordinator: TextZoomCoordinating + textZoomCoordinator: TextZoomCoordinating, + appDidFinishLaunchingStartTime: CFAbsoluteTime? ) { self.bookmarksDatabase = bookmarksDatabase self.bookmarksDatabaseCleaner = bookmarksDatabaseCleaner @@ -246,6 +249,7 @@ class MainViewController: UIViewController { self.fireproofing = fireproofing self.subscriptionCookieManager = subscriptionCookieManager self.textZoomCoordinator = textZoomCoordinator + self.appDidFinishLaunchingStartTime = appDidFinishLaunchingStartTime super.init(nibName: nil, bundle: nil) @@ -337,7 +341,14 @@ class MainViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - + defer { + if let appDidFinishLaunchingStartTime { + let launchTime = CFAbsoluteTimeGetCurrent() - appDidFinishLaunchingStartTime + Pixel.fire(pixel: .appDidShowUITime(time: Pixel.Event.BucketAggregation(number: launchTime)), + withAdditionalParameters: [PixelParameters.time: String(launchTime)]) + } + } + // Needs to be called here because sometimes the frames are not the expected size during didLoad refreshViewsBasedOnAddressBarPosition(appSettings.currentAddressBarPosition) diff --git a/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift b/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift index 3aa6866fb5..41398f8fc2 100644 --- a/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift +++ b/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift @@ -83,7 +83,8 @@ final class OnboardingDaxFavouritesTests: XCTestCase { voiceSearchHelper: MockVoiceSearchHelper(isSpeechRecognizerAvailable: true, voiceSearchEnabled: true), featureFlagger: MockFeatureFlagger(), subscriptionCookieManager: SubscriptionCookieManagerMock(), - textZoomCoordinator: MockTextZoomCoordinator() + textZoomCoordinator: MockTextZoomCoordinator(), + appDidFinishLaunchingStartTime: nil ) let window = UIWindow(frame: UIScreen.main.bounds) window.rootViewController = UIViewController() diff --git a/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift b/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift index 52e2da5b9a..7548fd0505 100644 --- a/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift +++ b/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift @@ -81,7 +81,8 @@ final class OnboardingNavigationDelegateTests: XCTestCase { voiceSearchHelper: MockVoiceSearchHelper(isSpeechRecognizerAvailable: true, voiceSearchEnabled: true), featureFlagger: MockFeatureFlagger(), subscriptionCookieManager: SubscriptionCookieManagerMock(), - textZoomCoordinator: MockTextZoomCoordinator()) + textZoomCoordinator: MockTextZoomCoordinator(), + appDidFinishLaunchingStartTime: nil) let window = UIWindow(frame: UIScreen.main.bounds) window.rootViewController = UIViewController() window.makeKeyAndVisible()