-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Fix #6692 - Moving most of the refactored leanplum A/B test changes from v26.0 to master #6736
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -463,7 +463,6 @@ class BrowserViewController: UIViewController { | |
|
||
// Setup onboarding user research for A/B testing | ||
onboardingUserResearch = OnboardingUserResearch() | ||
onboardingUserResearch?.lpVariableObserver() | ||
} | ||
|
||
fileprivate func setupConstraints() { | ||
|
@@ -2004,57 +2003,19 @@ extension BrowserViewController { | |
} | ||
|
||
private func onboardingUserResearchHelper(_ alwaysShow: Bool = false) { | ||
// Condition: Want to see our 1st time launched onboarding again | ||
// Our boolean variable shouldShow is used to present the onboarding | ||
// that was presented to the user during first launch | ||
if alwaysShow { | ||
showProperIntroVC() | ||
return | ||
} | ||
// Condition: Leanplum is disabled | ||
// If leanplum is not enabled then we set the value of onboarding research to true | ||
// True = .variant 1 which is our default Intro View | ||
// False = .variant 2 which is our new Intro View that we are A/B testing against | ||
// and get that from the server | ||
guard LeanPlumClient.shared.getSettings() != nil else { | ||
self.onboardingUserResearch?.updateValue(value: true) | ||
showProperIntroVC() | ||
return | ||
} | ||
// Condition: Update from leanplum server | ||
// Get the A/B test variant from leanplum server | ||
// and update onboarding user reasearch | ||
onboardingUserResearch?.updatedLPVariables = {(lpVariable) -> () in | ||
self.onboardingUserResearch?.updatedLPVariables = nil | ||
print("lp Variable from server \(String(describing: lpVariable?.boolValue()))") | ||
self.onboardingUserResearch?.updateTelemetry() | ||
self.onboardingUserResearch?.updateValue(value: lpVariable?.boolValue() ?? true) | ||
self.showProperIntroVC() | ||
} | ||
// Conditon: Leanplum server too slow | ||
// We don't want our users to be stuck on Onboarding | ||
// Wait 2 second and update the onboarding research variable | ||
// with true (True = .variant 1) | ||
// Ex. Internet connection is unstable due to which | ||
// leanplum isn't loading or taking too much time | ||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { | ||
guard self.onboardingUserResearch?.updatedLPVariables != nil else { | ||
return | ||
} | ||
Sentry.shared.send(message: "Failed to fetch A/B test variables from LP") | ||
self.onboardingUserResearch?.updatedLPVariables = nil | ||
self.onboardingUserResearch?.updateValue(value: true) | ||
// Setup user research closure and observer to fetch the updated LP Variables | ||
onboardingUserResearch?.updatedLPVariable = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First We setup the delegate |
||
self.showProperIntroVC() | ||
} | ||
onboardingUserResearch?.lpVariableObserver() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Second we call the observers so that closures can be called |
||
} | ||
|
||
private func showProperIntroVC() { | ||
// The onboarding screen type should always exist after | ||
// the screen is presented for the 1st time | ||
guard let onboardingScreenType = self.onboardingUserResearch?.onboardingScreenType else { | ||
return | ||
} | ||
let introViewController = IntroViewControllerV2(onboardingType: onboardingScreenType) | ||
let introViewController = IntroViewControllerV2() | ||
introViewController.didFinishClosure = { controller, fxaLoginFlow in | ||
self.profile.prefs.setInt(1, forKey: PrefsKeys.IntroSeen) | ||
controller.dismiss(animated: true) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,25 +7,35 @@ import Leanplum | |
import Shared | ||
|
||
struct LPVariables { | ||
static var showOnboardingScreen = LPVar.define("showOnboardingScreen", with: true) | ||
// Variable Used for AA test | ||
static var showOnboardingScreenAA = LPVar.define("showOnboardingScreen", with: true) | ||
// Variable Used for AB test | ||
static var showOnboardingScreenAB = LPVar.define("showOnboardingScreen_2", with: true) | ||
} | ||
|
||
// For LP variable below is the convention we follow | ||
// True = Current Onboarding Screen | ||
// False = New Onboarding Screen | ||
enum OnboardingScreenType: String { | ||
case versionV1 // Default | ||
case versionV2 // New version 2 | ||
case versionV1 | ||
case versionV2 | ||
|
||
static func from(boolValue: Bool) -> OnboardingScreenType { | ||
return boolValue ? .versionV1 : .versionV2 | ||
} | ||
} | ||
|
||
class OnboardingUserResearch { | ||
// Closure delegate | ||
var updatedLPVariables: ((LPVar?) -> Void)? | ||
var updatedLPVariable: (() -> Void)? | ||
// variable | ||
var lpVariable: LPVar? | ||
// Constants | ||
private let onboardingScreenTypeKey = "onboardingScreenTypeKey" | ||
// Saving user defaults | ||
private let defaults = UserDefaults.standard | ||
// Publicly accessible onboarding screen type | ||
var onboardingScreenType:OnboardingScreenType? { | ||
var onboardingScreenType: OnboardingScreenType? { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can the setter be private? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Making it private(set) won't allow HiddenSettings to clear AB variables. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. np, public is ok, i didn't know it was used elsewhere |
||
set(value) { | ||
if value == nil { | ||
defaults.removeObject(forKey: onboardingScreenTypeKey) | ||
|
@@ -42,23 +52,43 @@ class OnboardingUserResearch { | |
} | ||
|
||
// MARK: Initializer | ||
init(lpVariable: LPVar? = LPVariables.showOnboardingScreen) { | ||
init(lpVariable: LPVar? = LPVariables.showOnboardingScreenAB) { | ||
self.lpVariable = lpVariable | ||
} | ||
|
||
// MARK: public | ||
func lpVariableObserver() { | ||
Leanplum.onVariablesChanged { | ||
self.updatedLPVariables?(self.lpVariable) | ||
// Condition: Leanplum is disabled; use default intro view | ||
guard LeanPlumClient.shared.getSettings() != nil else { | ||
self.onboardingScreenType = .versionV1 | ||
self.updatedLPVariable?() | ||
return | ||
} | ||
// Condition: A/B test variables from leanplum server | ||
LeanPlumClient.shared.finishedStartingLeanplum = { | ||
let showScreenA = LPVariables.showOnboardingScreenAB?.boolValue() | ||
LeanPlumClient.shared.finishedStartingLeanplum = nil | ||
self.updateTelemetry() | ||
let screenType = OnboardingScreenType.from(boolValue: (showScreenA ?? true)) | ||
self.onboardingScreenType = screenType | ||
self.updatedLPVariable?() | ||
} | ||
// Condition: Leanplum server too slow; Show default onboarding. | ||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
guard LeanPlumClient.shared.finishedStartingLeanplum != nil else { | ||
return | ||
} | ||
let lpStartStatus = LeanPlumClient.shared.lpState | ||
var lpVariableValue: OnboardingScreenType = .versionV1 | ||
// Condition: LP has already started but we missed onStartLPVariable callback | ||
if case .started(startedState: _) = lpStartStatus , let boolValue = LPVariables.showOnboardingScreenAB?.boolValue() { | ||
lpVariableValue = boolValue ? .versionV1 : .versionV2 | ||
self.updateTelemetry() | ||
} | ||
self.updatedLPVariable = nil | ||
self.onboardingScreenType = lpVariableValue | ||
self.updatedLPVariable?() | ||
} | ||
} | ||
|
||
func updateValue(value: Bool) { | ||
// For LP variable below is the convention | ||
// we are going to follow | ||
// True = Current Onboarding Screen | ||
// False = New Onboarding Screen | ||
onboardingScreenType = value ? .versionV1 : .versionV2 | ||
} | ||
|
||
func updateTelemetry() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All this now resides in the onboarding user research file:
Client/UserResearch/OnboardingUserResearch.swift
https://github.com/mozilla-mobile/firefox-ios/pull/6736/files#diff-e716579b778d971b97c24f89b7163e0c