From 65779b46e9593d0d8d8147e5f29cb0ac0f14632a Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 16 May 2024 18:54:30 +0200 Subject: [PATCH 1/7] Updated defaults keys for future migration to BSK --- Core/UserDefaultsPropertyWrapper.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/UserDefaultsPropertyWrapper.swift b/Core/UserDefaultsPropertyWrapper.swift index 16e360f885..bf92b8a80b 100644 --- a/Core/UserDefaultsPropertyWrapper.swift +++ b/Core/UserDefaultsPropertyWrapper.swift @@ -84,9 +84,9 @@ public struct UserDefaultsWrapper { "com.duckduckgo.ios.autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary" case autofillImportViaSyncStart = "com.duckduckgo.ios.autofillImportViaSyncStart" case autofillSurveyEnabled = "com.duckduckgo.ios.autofillSurveyEnabled" - case autofillSearchDauDate = "com.duckduckgo.ios.autofillSearchDauDate" - case autofillFillDate = "com.duckduckgo.ios.autofillFillDate" - case autofillOnboardedUser = "com.duckduckgo.ios.autofillOnboardedUser" + case autofillSearchDauDate = "com.duckduckgo.app.autofill.SearchDauDate" + case autofillFillDate = "com.duckduckgo.app.autofill.FillDate" + case autofillOnboardedUser = "com.duckduckgo.app.autofill.OnboardedUser" // .v2 suffix added to fix https://app.asana.com/0/547792610048271/1206524375402369/f case featureFlaggingDidVerifyInternalUser = "com.duckduckgo.app.featureFlaggingDidVerifyInternalUser.v2" From a522319cae55f408e4b486e5345896f3c35add5a Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 16 May 2024 18:55:21 +0200 Subject: [PATCH 2/7] Bucketing temporarily moved here to not interfere with survey --- DuckDuckGo/AutofillPixelReporter.swift | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/AutofillPixelReporter.swift b/DuckDuckGo/AutofillPixelReporter.swift index f5eab8ca44..50aefb9a78 100644 --- a/DuckDuckGo/AutofillPixelReporter.swift +++ b/DuckDuckGo/AutofillPixelReporter.swift @@ -101,14 +101,29 @@ final class AutofillPixelReporter { for pixel in pixels { switch pixel { case .autofillLoginsStacked: - let bucket = (try? secureVault?.accountsCountBucket()) ?? "" - DailyPixel.fire(pixel: pixel, withAdditionalParameters: [PixelParameters.countBucket: bucket]) + if let count = try? vault()?.accountsCount() { + DailyPixel.fire(pixel: pixel, withAdditionalParameters: [PixelParameters.countBucket: accountsBucketNameFrom(count: count)]) + } default: DailyPixel.fire(pixel: pixel) } } } + public func accountsBucketNameFrom(count: Int) -> String { + if count == 0 { + return "none" + } else if count < 4 { + return "few" + } else if count < 11 { + return "some" + } else if count < 50 { + return "many" + } else { + return "lots" + } + } + private func shouldFireActiveUserPixel() -> Bool { let today = Date() if today.isSameDay(autofillSearchDauDate) && today.isSameDay(autofillFillDate) { From 7ac6acf5b9da998cd65322219cdba6f930a8aaff Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 16 May 2024 18:56:14 +0200 Subject: [PATCH 3/7] Initialisation of vault updated to only be on demand --- DuckDuckGo/AutofillPixelReporter.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo/AutofillPixelReporter.swift b/DuckDuckGo/AutofillPixelReporter.swift index 50aefb9a78..834fb931bb 100644 --- a/DuckDuckGo/AutofillPixelReporter.swift +++ b/DuckDuckGo/AutofillPixelReporter.swift @@ -33,14 +33,14 @@ final class AutofillPixelReporter { var autofillOnboardedUser: Bool private let statisticsStorage: StatisticsStore - private let secureVault: (any AutofillSecureVault)? + private var secureVault: (any AutofillSecureVault)? enum EventType { case fill case searchDAU } - init(statisticsStorage: StatisticsStore = StatisticsUserDefaults(), secureVault: (any AutofillSecureVault)? = try? AutofillSecureVaultFactory.makeVault(reporter: SecureVaultReporter.shared)) { + init(statisticsStorage: StatisticsStore = StatisticsUserDefaults(), secureVault: (any AutofillSecureVault)? = nil) { self.statisticsStorage = statisticsStorage self.secureVault = secureVault @@ -56,6 +56,13 @@ final class AutofillPixelReporter { NotificationCenter.default.addObserver(self, selector: #selector(didReceiveFillEvent), name: .autofillFillEvent, object: nil) } + private func vault() -> (any AutofillSecureVault)? { + if secureVault == nil { + secureVault = try? AutofillSecureVaultFactory.makeVault(reporter: SecureVaultReporter.shared) + } + return secureVault + } + @objc private func didReceiveSearchDAU() { guard !Date().isSameDay(autofillSearchDauDate) else { @@ -133,7 +140,7 @@ final class AutofillPixelReporter { } private func shouldFireEnabledUserPixel() -> Bool { - if Date().isSameDay(autofillSearchDauDate), let count = try? secureVault?.accountsCount(), count >= 10 { + if Date().isSameDay(autofillSearchDauDate), let count = try? vault()?.accountsCount(), count >= 10 { return true } return false @@ -147,7 +154,7 @@ final class AutofillPixelReporter { let pastWeek = Date().addingTimeInterval(.days(-7)) if installDate >= pastWeek { - if let count = try? secureVault?.accountsCount(), count > 0 { + if let count = try? vault()?.accountsCount(), count > 0 { autofillOnboardedUser = true return true } From 1ac8dadff74bb84037eba9f8a1e3072c1146fbfb Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 16 May 2024 18:57:02 +0200 Subject: [PATCH 4/7] Tweak to onboarded pixel logic --- DuckDuckGo/AppDelegate.swift | 2 ++ DuckDuckGo/AutofillPixelReporter.swift | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index f783373324..3e6a5d41b7 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -673,6 +673,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } AppDependencyProvider.shared.userBehaviorMonitor.handleAction(.reopenApp) + + autofillPixelReporter.checkIfOnboardedUser() } func applicationDidEnterBackground(_ application: UIApplication) { diff --git a/DuckDuckGo/AutofillPixelReporter.swift b/DuckDuckGo/AutofillPixelReporter.swift index 834fb931bb..8d724ac803 100644 --- a/DuckDuckGo/AutofillPixelReporter.swift +++ b/DuckDuckGo/AutofillPixelReporter.swift @@ -46,9 +46,7 @@ final class AutofillPixelReporter { createNotificationObservers() - if shouldFireOnboardedUserPixel() { - DailyPixel.fire(pixel: .autofillOnboardedUser) - } + checkIfOnboardedUser() } private func createNotificationObservers() { @@ -85,6 +83,14 @@ final class AutofillPixelReporter { firePixels(pixelsToFireFor(.fill)) } + func checkIfOnboardedUser() { + guard !autofillOnboardedUser else { return } + + if shouldFireOnboardedUserPixel() { + firePixels([.autofillOnboardedUser]) + } + } + func pixelsToFireFor(_ type: EventType) -> [Pixel.Event] { var pixelsToFire: [Pixel.Event] = [] From c7e77b286097e68ed8620eed8da4a6f2d76b7082 Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 16 May 2024 18:57:41 +0200 Subject: [PATCH 5/7] Change .autofillLoginsStacked to always fire with .autofillActiveUser --- DuckDuckGo/AutofillPixelReporter.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/AutofillPixelReporter.swift b/DuckDuckGo/AutofillPixelReporter.swift index 8d724ac803..4cd08757f0 100644 --- a/DuckDuckGo/AutofillPixelReporter.swift +++ b/DuckDuckGo/AutofillPixelReporter.swift @@ -96,15 +96,16 @@ final class AutofillPixelReporter { if shouldFireActiveUserPixel() { pixelsToFire.append(.autofillActiveUser) + pixelsToFire.append(.autofillLoginsStacked) } switch type { - case .fill: - pixelsToFire.append(.autofillLoginsStacked) case .searchDAU: if shouldFireEnabledUserPixel() { pixelsToFire.append(.autofillEnabledUser) } + default: + break } return pixelsToFire From be50ea9922af99790491b06ccd8a9b2a4c502080 Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 16 May 2024 20:04:00 +0200 Subject: [PATCH 6/7] Updated unit tests --- DuckDuckGoTests/AutofillPixelReporterTests.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGoTests/AutofillPixelReporterTests.swift b/DuckDuckGoTests/AutofillPixelReporterTests.swift index 7079ccd819..dde4b68021 100644 --- a/DuckDuckGoTests/AutofillPixelReporterTests.swift +++ b/DuckDuckGoTests/AutofillPixelReporterTests.swift @@ -66,28 +66,28 @@ final class AutofillPixelReporterTests: XCTestCase { XCTAssertEqual(autofillPixelReporter.pixelsToFireFor(.fill).count, 1) } - func testWhenUserSearchDauIsTodayAndAutofillDateIsTodayAndEventTypeIsSearchDauAndAccountsCountIsLessThanTenThenOnePixelWillBeFired() { + func testWhenUserSearchDauIsTodayAndAutofillDateIsTodayAndEventTypeIsSearchDauAndAccountsCountIsLessThanTenThenTwoPixelWillBeFired() { autofillPixelReporter.autofillSearchDauDate = Date() autofillPixelReporter.autofillFillDate = Date() createAccountsInVault(count: 4) - XCTAssertEqual(autofillPixelReporter.pixelsToFireFor(.searchDAU).count, 1) + XCTAssertEqual(autofillPixelReporter.pixelsToFireFor(.searchDAU).count, 2) } - func testWhenUserSearchDauIsTodayAndAutofillDateIsTodayAndEventTypeIsSearchDauAndAccountsCountIsTenThenTwoPixelsWillBeFired() { + func testWhenUserSearchDauIsTodayAndAutofillDateIsTodayAndEventTypeIsSearchDauAndAccountsCountIsTenThenThreePixelsWillBeFired() { autofillPixelReporter.autofillSearchDauDate = Date() autofillPixelReporter.autofillFillDate = Date() createAccountsInVault(count: 10) - XCTAssertEqual(autofillPixelReporter.pixelsToFireFor(.searchDAU).count, 2) + XCTAssertEqual(autofillPixelReporter.pixelsToFireFor(.searchDAU).count, 3) } - func testWhenUserSearchDauIsTodayAndAutofillDateIsTodayAndEventTypeIsSearchDauAndAccountsCountIsGreaterThanTenThenTwoPixelsWillBeFired() { + func testWhenUserSearchDauIsTodayAndAutofillDateIsTodayAndEventTypeIsSearchDauAndAccountsCountIsGreaterThanTenThenThreePixelsWillBeFired() { autofillPixelReporter.autofillSearchDauDate = Date() autofillPixelReporter.autofillFillDate = Date() createAccountsInVault(count: 15) - XCTAssertEqual(autofillPixelReporter.pixelsToFireFor(.searchDAU).count, 2) + XCTAssertEqual(autofillPixelReporter.pixelsToFireFor(.searchDAU).count, 3) } func testWhenUserSearchDauIsTodayAndAutofillDateIsNotTodayAndEventTypeIsSearchDauAndAccountsCountIsLessThanTenThenNoPixelsWillBeFired() { From fb801c7b8b6fd02b6f756b1b9d44642203a3cd67 Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 16 May 2024 20:44:54 +0200 Subject: [PATCH 7/7] Updated unit tests --- DuckDuckGoTests/AutofillPixelReporterTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DuckDuckGoTests/AutofillPixelReporterTests.swift b/DuckDuckGoTests/AutofillPixelReporterTests.swift index dde4b68021..0c915d5b18 100644 --- a/DuckDuckGoTests/AutofillPixelReporterTests.swift +++ b/DuckDuckGoTests/AutofillPixelReporterTests.swift @@ -59,11 +59,11 @@ final class AutofillPixelReporterTests: XCTestCase { XCTAssertTrue(autofillPixelReporter.pixelsToFireFor(.searchDAU).isEmpty) } - func testWhenUserSearchDauIsNotTodayAndEventTypeIsFillThenOnePixelWillBeFired() { + func testWhenUserSearchDauIsNotTodayAndEventTypeIsFillThenNoPixelsWillBeFired() { autofillPixelReporter.autofillSearchDauDate = Date().addingTimeInterval(-2 * 60 * 60 * 24) autofillPixelReporter.autofillFillDate = Date().addingTimeInterval(-2 * 60 * 60 * 24) - XCTAssertEqual(autofillPixelReporter.pixelsToFireFor(.fill).count, 1) + XCTAssertTrue(autofillPixelReporter.pixelsToFireFor(.fill).isEmpty) } func testWhenUserSearchDauIsTodayAndAutofillDateIsTodayAndEventTypeIsSearchDauAndAccountsCountIsLessThanTenThenTwoPixelWillBeFired() {