Skip to content

Commit

Permalink
fire experiment pixels (#3622)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/1204186595873227/1208775176602791/f
Tech Design URL:
https://app.asana.com/0/1204186595873227/1208682592686299
CC:

**Description**: Implements PixelExperimentKit to provide Experiment
metrics API to clients and fires pixel for preset experiment metrics
  • Loading branch information
SabrinaTardio authored Dec 5, 2024
1 parent 170e7ca commit 7b2a893
Show file tree
Hide file tree
Showing 18 changed files with 85 additions and 44 deletions.
16 changes: 15 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1689,6 +1689,8 @@
562984712AC469E400AC20EB /* SyncPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5629846E2AC4610100AC20EB /* SyncPreferencesTests.swift */; };
56406D4B2C636A8900BF8FA2 /* SpecialPagesUserScriptExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56406D4A2C636A8900BF8FA2 /* SpecialPagesUserScriptExtension.swift */; };
56406D4C2C636A8900BF8FA2 /* SpecialPagesUserScriptExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56406D4A2C636A8900BF8FA2 /* SpecialPagesUserScriptExtension.swift */; };
5641734B2CFE168700F4B716 /* PixelExperimentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5641734A2CFE168700F4B716 /* PixelExperimentKit */; };
5641734D2CFE169400F4B716 /* PixelExperimentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5641734C2CFE169400F4B716 /* PixelExperimentKit */; };
56534DED29DF252C00121467 /* CapturingDefaultBrowserProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56534DEC29DF252C00121467 /* CapturingDefaultBrowserProvider.swift */; };
56534DEE29DF252C00121467 /* CapturingDefaultBrowserProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56534DEC29DF252C00121467 /* CapturingDefaultBrowserProvider.swift */; };
565E46E02B2725DD0013AC2A /* CriticalPathsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 565E46DF2B2725DD0013AC2A /* CriticalPathsTests.swift */; };
Expand Down Expand Up @@ -5083,6 +5085,7 @@
567A23C52C7F75BB0010F66C /* SpecialErrorPages in Frameworks */,
372217822B33380700B8E9C2 /* TestUtils in Frameworks */,
3706FCAA293F65D500E42796 /* UserScript in Frameworks */,
5641734D2CFE169400F4B716 /* PixelExperimentKit in Frameworks */,
85E2BBD02B8F534A00DBEC7A /* History in Frameworks */,
4BF97AD52B43C43F00EB4240 /* NetworkProtection in Frameworks */,
560EB9372C78974C0080DBC8 /* Onboarding in Frameworks */,
Expand Down Expand Up @@ -5317,6 +5320,7 @@
CD3301242C8870DF009AA127 /* MaliciousSiteProtection in Frameworks */,
C18BF9CC2C73678500ED6B8A /* Freemium in Frameworks */,
F1DF95E32BD1807C0045E591 /* Crashes in Frameworks */,
5641734B2CFE168700F4B716 /* PixelExperimentKit in Frameworks */,
560EB9352C7897370080DBC8 /* Onboarding in Frameworks */,
85E2BBCE2B8F534000DBEC7A /* History in Frameworks */,
1EA7B8D32B7E078C000330A4 /* SubscriptionUI in Frameworks */,
Expand Down Expand Up @@ -10034,6 +10038,7 @@
CBECDB8D2CDBD62C005B8B87 /* PageRefreshMonitor */,
CBECDB8F2CDBD631005B8B87 /* BrokenSitePrompt */,
37DF37062CF38B9F005ED34B /* PrivacyStats */,
5641734C2CFE169400F4B716 /* PixelExperimentKit */,
);
productName = DuckDuckGo;
productReference = 3706FD05293F65D500E42796 /* DuckDuckGo App Store.app */;
Expand Down Expand Up @@ -10514,6 +10519,7 @@
CBECDB892CDBD616005B8B87 /* PageRefreshMonitor */,
CBECDB8B2CDBD61C005B8B87 /* BrokenSitePrompt */,
37DF37042CF38B96005ED34B /* PrivacyStats */,
5641734A2CFE168700F4B716 /* PixelExperimentKit */,
);
productName = DuckDuckGo;
productReference = AA585D7E248FD31100E9A3E2 /* DuckDuckGo.app */;
Expand Down Expand Up @@ -15365,7 +15371,7 @@
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 217.0.2;
version = 218.0.0;
};
};
9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down Expand Up @@ -15820,6 +15826,14 @@
package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */;
productName = Onboarding;
};
5641734A2CFE168700F4B716 /* PixelExperimentKit */ = {
isa = XCSwiftPackageProductDependency;
productName = PixelExperimentKit;
};
5641734C2CFE169400F4B716 /* PixelExperimentKit */ = {
isa = XCSwiftPackageProductDependency;
productName = PixelExperimentKit;
};
567A23C02C7F71570010F66C /* SpecialErrorPages */ = {
isa = XCSwiftPackageProductDependency;
package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "f0755fbb3309c93c8490dc8bbdfb7e2e7613bef6",
"version" : "217.0.2"
"revision" : "e5d390c8559fbe7b1ca67fd3982c91bcc0437d60",
"version" : "218.0.0"
}
},
{
Expand Down Expand Up @@ -75,7 +75,7 @@
{
"identity" : "lottie-spm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/airbnb/lottie-spm",
"location" : "https://github.com/airbnb/lottie-spm.git",
"state" : {
"revision" : "1d29eccc24cc8b75bff9f6804155112c0ffc9605",
"version" : "4.4.3"
Expand Down
4 changes: 3 additions & 1 deletion DuckDuckGo/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import MetricKit
import Networking
import Persistence
import PixelKit
import PixelExperimentKit
import ServiceManagement
import SyncDataProviders
import UserNotifications
Expand Down Expand Up @@ -282,7 +283,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
keyValueStore: UserDefaults.appConfiguration,
actionHandler: FeatureFlagOverridesPublishingHandler<FeatureFlag>()
),
experimentManager: ExperimentCohortsManager(store: ExperimentsDataStore()),
experimentManager: ExperimentCohortsManager(store: ExperimentsDataStore(), fireCohortAssigned: PixelKit.fireExperimentEnrollmentPixel(subfeatureID:experiment:)),
for: FeatureFlag.self
)

Expand Down Expand Up @@ -331,6 +332,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
#else
privacyStats = PrivacyStats(databaseProvider: PrivacyStatsDatabase())
#endif
PixelKit.configureExperimentKit(featureFlagger: featureFlagger, eventTracker: ExperimentEventTracker(store: UserDefaults.appConfiguration))
}

func applicationWillFinishLaunching(_ notification: Notification) {
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/HomePage/View/ContinueSetUpView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ extension HomePage.Views {
.onAppear {
if featureType == .dock {
PixelKit.fire(GeneralPixel.addToDockNewTabPageCardPresented,
frequency: .unique,
frequency: .uniqueByName,
includeAppVersionParameter: false)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr

PixelKit.fire(
NetworkProtectionPixelEvent.networkProtectionNewUser,
frequency: .unique,
frequency: .uniqueByName,
includeAppVersionParameter: true) { [weak self] fired, error in
guard let self, error == nil, fired else { return }
self.defaults.vpnFirstEnabled = PixelKit.pixelLastFireDate(event: NetworkProtectionPixelEvent.networkProtectionNewUser)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ extension HomePage.Models.ContinueSetUpModel: NewTabPageNextStepsCardsProviding
return
}
PixelKit.fire(GeneralPixel.addToDockNewTabPageCardPresented,
frequency: .unique,
frequency: .uniqueByName,
includeAppVersionParameter: false)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,35 +57,35 @@ final class OnboardingPixelReporter: OnboardingSearchSuggestionsPixelReporting,
}

func trackSiteSuggetionOptionTapped() {
fire(ContextualOnboardingPixel.siteSuggetionOptionTapped, .unique)
fire(ContextualOnboardingPixel.siteSuggetionOptionTapped, .uniqueByName)
}

func trackSearchSuggetionOptionTapped() {
fire(ContextualOnboardingPixel.searchSuggetionOptionTapped, .unique)
fire(ContextualOnboardingPixel.searchSuggetionOptionTapped, .uniqueByName)
}
}

extension OnboardingPixelReporter: OnboardingAddressBarReporting {
func trackPrivacyDashboardOpened() {
if onboardingStateProvider.state != .onboardingCompleted {
fire(ContextualOnboardingPixel.onboardingPrivacyDashboardOpened, .unique)
fire(ContextualOnboardingPixel.onboardingPrivacyDashboardOpened, .uniqueByName)
}
}

func trackAddressBarTypedIn() {
if onboardingStateProvider.state == .showTryASearch {
fire(ContextualOnboardingPixel.onboardingSearchCustom, .unique)
fire(ContextualOnboardingPixel.onboardingSearchCustom, .uniqueByName)
}
if onboardingStateProvider.state == .showTryASite {
fire(ContextualOnboardingPixel.onboardingVisitSiteCustom, .unique)
fire(ContextualOnboardingPixel.onboardingVisitSiteCustom, .uniqueByName)
}
}

func trackSiteVisited() {
let key = "onboarding.website-visited"
let siteVisited = userDefaults.bool(forKey: key)
if siteVisited {
fire(ContextualOnboardingPixel.secondSiteVisited, .unique)
fire(ContextualOnboardingPixel.secondSiteVisited, .uniqueByName)
} else {
userDefaults.set(true, forKey: key)
}
Expand All @@ -95,21 +95,21 @@ extension OnboardingPixelReporter: OnboardingAddressBarReporting {
extension OnboardingPixelReporter: OnboardingFireReporting {
func trackFireButtonPressed() {
if onboardingStateProvider.state != .onboardingCompleted {
fire(ContextualOnboardingPixel.onboardingFireButtonPressed, .unique)
fire(ContextualOnboardingPixel.onboardingFireButtonPressed, .uniqueByName)
}
}
}

extension OnboardingPixelReporter: OnboardingDialogsReporting {
func trackLastDialogShown() {
fire(ContextualOnboardingPixel.onboardingFinished, .unique)
fire(ContextualOnboardingPixel.onboardingFinished, .uniqueByName)
}

func trackFireButtonSkipped() {
fire(ContextualOnboardingPixel.onboardingFireButtonPromptSkipPressed, .unique)
fire(ContextualOnboardingPixel.onboardingFireButtonPromptSkipPressed, .uniqueByName)
}

func trackFireButtonTryIt() {
fire(ContextualOnboardingPixel.onboardingFireButtonTryItPressed, .unique)
fire(ContextualOnboardingPixel.onboardingFireButtonTryItPressed, .uniqueByName)
}
}
2 changes: 1 addition & 1 deletion DuckDuckGo/Preferences/View/PreferencesRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ enum Preferences {
case .activateAddEmailClick:
PixelKit.fire(PrivacyProPixel.privacyProRestorePurchaseEmailStart, frequency: .legacyDailyAndCount)
case .postSubscriptionAddEmailClick:
PixelKit.fire(PrivacyProPixel.privacyProWelcomeAddDevice, frequency: .unique)
PixelKit.fire(PrivacyProPixel.privacyProWelcomeAddDevice, frequency: .uniqueByName)
case .restorePurchaseStoreClick:
PixelKit.fire(PrivacyProPixel.privacyProRestorePurchaseStoreStart, frequency: .legacyDailyAndCount)
case .addDeviceEnterEmail:
Expand Down
12 changes: 11 additions & 1 deletion DuckDuckGo/Statistics/ATB/StatisticsLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Foundation
import BrowserServicesKit
import Networking
import PixelKit
import PixelExperimentKit
import os.log

final class StatisticsLoader {
Expand All @@ -34,15 +35,21 @@ final class StatisticsLoader {
private let attributionPixelHandler: InstallationAttributionsPixelHandler
private let parser = AtbParser()
private var isAppRetentionRequestInProgress = false
private let fireSearchExperimentPixels: () -> Void
private let fireAppRetentionExperimentPixels: () -> Void

init(
statisticsStore: StatisticsStore = LocalStatisticsStore(),
emailManager: EmailManager = EmailManager(),
attributionPixelHandler: InstallationAttributionsPixelHandler = AppInstallationAttributionPixelHandler()
attributionPixelHandler: InstallationAttributionsPixelHandler = AppInstallationAttributionPixelHandler(),
fireAppRetentionExperimentPixels: @escaping () -> Void = PixelKit.fireAppRetentionExperimentPixels,
fireSearchExperimentPixels: @escaping () -> Void = PixelKit.fireSearchExperimentPixels
) {
self.statisticsStore = statisticsStore
self.emailManager = emailManager
self.attributionPixelHandler = attributionPixelHandler
self.fireSearchExperimentPixels = fireSearchExperimentPixels
self.fireAppRetentionExperimentPixels = fireAppRetentionExperimentPixels
}

func refreshRetentionAtb(isSearch: Bool, completion: @escaping Completion = {}) {
Expand All @@ -57,13 +64,16 @@ final class StatisticsLoader {
}
PixelExperiment.fireSerpPixel()
PixelExperiment.fireOnboardingSearchPerformed5to7Pixel()
self.fireSearchExperimentPixels()
if NSApp.runType == .normal {
self.fireDailyOsVersionCounterPixel()
}
self.fireDockPixel()
} else if !self.statisticsStore.isAppRetentionFiredToday {
self.refreshAppRetentionAtb(completion: completion)
self.fireAppRetentionExperimentPixels()
} else {
self.fireAppRetentionExperimentPixels()
completion()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature {
PixelKit.fire(PrivacyProPixel.privacyProPurchaseSuccess, frequency: .legacyDailyAndCount)
sendFreemiumSubscriptionPixelIfFreemiumActivated()
saveSubscriptionUpgradeTimestampIfFreemiumActivated()
PixelKit.fire(PrivacyProPixel.privacyProSubscriptionActivated, frequency: .unique)
PixelKit.fire(PrivacyProPixel.privacyProSubscriptionActivated, frequency: .uniqueByName)
subscriptionSuccessPixelHandler.fireSuccessfulSubscriptionAttributionPixel()
sendSubscriptionUpgradeFromFreemiumNotificationIfFreemiumActivated()
await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: purchaseUpdate)
Expand Down Expand Up @@ -354,14 +354,14 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature {

switch featureSelection.productFeature {
case .networkProtection:
PixelKit.fire(PrivacyProPixel.privacyProWelcomeVPN, frequency: .unique)
PixelKit.fire(PrivacyProPixel.privacyProWelcomeVPN, frequency: .uniqueByName)
notificationCenter.post(name: .ToggleNetworkProtectionInMainWindow, object: self, userInfo: nil)
case .dataBrokerProtection:
PixelKit.fire(PrivacyProPixel.privacyProWelcomePersonalInformationRemoval, frequency: .unique)
PixelKit.fire(PrivacyProPixel.privacyProWelcomePersonalInformationRemoval, frequency: .uniqueByName)
notificationCenter.post(name: .openPersonalInformationRemoval, object: self, userInfo: nil)
await uiHandler.showTab(with: .dataBrokerProtection)
case .identityTheftRestoration, .identityTheftRestorationGlobal:
PixelKit.fire(PrivacyProPixel.privacyProWelcomeIdentityRestoration, frequency: .unique)
PixelKit.fire(PrivacyProPixel.privacyProWelcomeIdentityRestoration, frequency: .uniqueByName)
let url = subscriptionManager.url(for: .identityTheftRestoration)
await uiHandler.showTab(with: .identityTheftRestoration(url))
case .unknown:
Expand Down Expand Up @@ -402,12 +402,12 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature {
}

func subscriptionsAddEmailSuccess(params: Any, original: WKScriptMessage) async -> Encodable? {
PixelKit.fire(PrivacyProPixel.privacyProAddEmailSuccess, frequency: .unique)
PixelKit.fire(PrivacyProPixel.privacyProAddEmailSuccess, frequency: .uniqueByName)
return nil
}

func subscriptionsWelcomeFaqClicked(params: Any, original: WKScriptMessage) async -> Encodable? {
PixelKit.fire(PrivacyProPixel.privacyProWelcomeFAQClick, frequency: .unique)
PixelKit.fire(PrivacyProPixel.privacyProWelcomeFAQClick, frequency: .uniqueByName)
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/DataBrokerProtection/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ let package = Package(
targets: ["DataBrokerProtection"])
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "217.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "218.0.0"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../AppKitExtensions"),
.package(path: "../XPCHelper"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public class FreemiumDBPExperimentPixelHandler: EventMapping<FreemiumDBPExperime
super.init { event, _, params, _ in
switch event {
case .subscription:
PixelKit.fire(event, frequency: .unique, withAdditionalParameters: params)
PixelKit.fire(event, frequency: .uniqueByName, withAdditionalParameters: params)
default:
PixelKit.fire(event, frequency: .unique)
PixelKit.fire(event, frequency: .uniqueByName)
}

}
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/FeatureFlags/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
targets: ["FeatureFlags"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "217.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "218.0.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/NetworkProtectionMac/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
.library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "217.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "218.0.0"),
.package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"),
.package(path: "../AppLauncher"),
.package(path: "../UDSHelper"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SubscriptionUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let package = Package(
targets: ["SubscriptionUI"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "217.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "218.0.0"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../FeatureFlags")
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ final class NetworkProtectionPixelEventTests: XCTestCase {
file: #filePath,
line: #line)
fire(NetworkProtectionPixelEvent.networkProtectionNewUser,
frequency: .unique,
frequency: .uniqueByName,
and: .expect(pixelName: "m_mac_netp_daily_active_u"),
file: #filePath,
line: #line)
Expand Down
Loading

0 comments on commit 7b2a893

Please sign in to comment.