Skip to content

Commit

Permalink
Add install pixel (#3656)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/1199333091098016/1208873240363373/f
Tech Design URL:
CC:

Description:

This PR adds an m.install.ios.* pixel that is sent when calling the exti URL.
  • Loading branch information
samsymons authored Dec 4, 2024
1 parent edf899d commit a58b9c9
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 3 deletions.
4 changes: 3 additions & 1 deletion Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import NetworkProtection
extension Pixel {

public enum Event {


case appInstall
case appLaunch
case refreshPressed
case pullToRefresh
Expand Down Expand Up @@ -914,6 +915,7 @@ extension Pixel.Event {

public var name: String {
switch self {
case .appInstall: return "m_install"
case .appLaunch: return "ml"
case .refreshPressed: return "m_r"
case .pullToRefresh: return "m_pull-to-reload"
Expand Down
20 changes: 19 additions & 1 deletion Core/StatisticsLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,18 @@ public class StatisticsLoader {
private let parser = AtbParser()
private let atbPresenceFileMarker = BoolFileMarker(name: .isATBPresent)
private let inconsistencyMonitoring: StatisticsStoreInconsistencyMonitoring
private let pixelFiring: PixelFiring.Type

init(statisticsStore: StatisticsStore = StatisticsUserDefaults(),
returnUserMeasurement: ReturnUserMeasurement = KeychainReturnUserMeasurement(),
usageSegmentation: UsageSegmenting = UsageSegmentation(),
inconsistencyMonitoring: StatisticsStoreInconsistencyMonitoring = StorageInconsistencyMonitor()) {
inconsistencyMonitoring: StatisticsStoreInconsistencyMonitoring = StorageInconsistencyMonitor(),
pixelFiring: PixelFiring.Type = Pixel.self) {
self.statisticsStore = statisticsStore
self.returnUserMeasurement = returnUserMeasurement
self.usageSegmentation = usageSegmentation
self.inconsistencyMonitoring = inconsistencyMonitoring
self.pixelFiring = pixelFiring
}

public func load(completion: @escaping Completion = {}) {
Expand Down Expand Up @@ -94,6 +97,7 @@ public class StatisticsLoader {
completion()
return
}
self.fireInstallPixel()
self.statisticsStore.installDate = Date()
self.statisticsStore.atb = atb.version
self.returnUserMeasurement.installCompletedWithATB(atb)
Expand All @@ -102,6 +106,20 @@ public class StatisticsLoader {
}
}

private func fireInstallPixel() {
let formattedLocale = Locale.current.localeIdentifierAsJsonFormat
let isReinstall = String(statisticsStore.variant == VariantIOS.returningUser.name)
let parameters = [
"locale": formattedLocale,
"reinstall": isReinstall
]
pixelFiring.fire(.appInstall, withAdditionalParameters: parameters, includedParameters: [.appVersion], onComplete: { error in
if let error {
Logger.general.error("Install pixel failed with error: \(error.localizedDescription, privacy: .public)")
}
})
}

private func createATBFileMarker() {
atbPresenceFileMarker?.mark()
}
Expand Down
21 changes: 20 additions & 1 deletion DuckDuckGoTests/StatisticsLoaderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,26 @@ class StatisticsLoaderTests: XCTestCase {

var mockStatisticsStore: StatisticsStore!
var mockUsageSegmentation: MockUsageSegmentation!
var mockPixelFiring: PixelFiringMock.Type!
var testee: StatisticsLoader!

override func setUpWithError() throws {
try super.setUpWithError()

PixelFiringMock.tearDown()

mockPixelFiring = PixelFiringMock.self
mockStatisticsStore = MockStatisticsStore()
mockUsageSegmentation = MockUsageSegmentation()
testee = StatisticsLoader(statisticsStore: mockStatisticsStore,
usageSegmentation: mockUsageSegmentation,
inconsistencyMonitoring: MockStatisticsStoreInconsistencyMonitoring())
inconsistencyMonitoring: MockStatisticsStoreInconsistencyMonitoring(),
pixelFiring: mockPixelFiring)
}

override func tearDown() {
HTTPStubs.removeAllStubs()
PixelFiringMock.tearDown()
super.tearDown()
}

Expand Down Expand Up @@ -270,6 +276,19 @@ class StatisticsLoaderTests: XCTestCase {
waitForExpectations(timeout: 5, handler: nil)
}

func testWhenInstallStatisticsRequestedThenInstallPixelIsFired() {
loadSuccessfulExiStub()

let testExpectation = expectation(description: "refresh complete")
testee.refreshAppRetentionAtb {
Thread.sleep(forTimeInterval: .seconds(0.1))
testExpectation.fulfill()
}

wait(for: [testExpectation], timeout: 5.0)
XCTAssertEqual(mockPixelFiring.lastPixelName, Pixel.Event.appInstall.name)
}

func loadSuccessfulAtbStub() {
stub(condition: isHost(URL.atb.host!)) { _ in
let path = OHPathForFile("MockFiles/atb.json", type(of: self))!
Expand Down

0 comments on commit a58b9c9

Please sign in to comment.