Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tweaks to autofill pixels for KPIs #2865

Merged
merged 7 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Core/UserDefaultsPropertyWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ public struct UserDefaultsWrapper<T> {
"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"
amddg44 marked this conversation as resolved.
Show resolved Hide resolved
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"
Expand Down
2 changes: 2 additions & 0 deletions DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

AppDependencyProvider.shared.userBehaviorMonitor.handleAction(.reopenApp)

autofillPixelReporter.checkIfOnboardedUser()
}

func applicationDidEnterBackground(_ application: UIApplication) {
Expand Down
51 changes: 40 additions & 11 deletions DuckDuckGo/AutofillPixelReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,34 @@ 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) {
amddg44 marked this conversation as resolved.
Show resolved Hide resolved
self.statisticsStorage = statisticsStorage
self.secureVault = secureVault

createNotificationObservers()

if shouldFireOnboardedUserPixel() {
DailyPixel.fire(pixel: .autofillOnboardedUser)
}
checkIfOnboardedUser()
}

private func createNotificationObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveSearchDAU), name: .searchDAU, object: nil)
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 {
Expand All @@ -78,20 +83,29 @@ final class AutofillPixelReporter {
firePixels(pixelsToFireFor(.fill))
}

func checkIfOnboardedUser() {
amddg44 marked this conversation as resolved.
Show resolved Hide resolved
guard !autofillOnboardedUser else { return }

if shouldFireOnboardedUserPixel() {
firePixels([.autofillOnboardedUser])
}
}

func pixelsToFireFor(_ type: EventType) -> [Pixel.Event] {
var pixelsToFire: [Pixel.Event] = []

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
Expand All @@ -101,14 +115,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 {
amddg44 marked this conversation as resolved.
Show resolved Hide resolved
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) {
Expand All @@ -118,7 +147,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
Expand All @@ -132,7 +161,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
}
Expand Down
16 changes: 8 additions & 8 deletions DuckDuckGoTests/AutofillPixelReporterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,35 +59,35 @@ 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 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() {
Expand Down
Loading