Skip to content

Commit

Permalink
[NT-1023] Add Temporary Checkout Completed event (#1127)
Browse files Browse the repository at this point in the history
* Add temporary checkout completed event, refactor optimizely functions

* Fix tests

* Move Optimizely experiment tracking to separate tests

* Rename to App Completed Checkout
  • Loading branch information
justinswart authored Mar 31, 2020
1 parent d80b606 commit 4bdc931
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 98 deletions.
19 changes: 2 additions & 17 deletions Kickstarter-iOS/ViewModels/AppDelegateViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -641,11 +641,7 @@ public final class AppDelegateViewModel: AppDelegateViewModelType, AppDelegateVi

self.applicationDidEnterBackgroundProperty.signal
.observeValues {
let (properties, eventTags) = optimizelyTrackingAttributesAndEventTags(
with: AppEnvironment.current.currentUser,
project: nil,
refTag: nil
)
let (properties, eventTags) = optimizelyTrackingAttributesAndEventTags()

try? AppEnvironment.current.optimizelyClient?
.track(
Expand Down Expand Up @@ -1125,19 +1121,8 @@ private func shouldGoToLandingPage() -> Bool {
return false
}

let userAttributes = optimizelyUserAttributes(
with: AppEnvironment.current.currentUser,
project: nil,
refTag: nil
)

let optimizelyVariant = AppEnvironment.current.optimizelyClient?
.variant(
for: OptimizelyExperiment.Key.nativeOnboarding,
userId: deviceIdentifier(uuid: UUID()),
isAdmin: AppEnvironment.current.currentUser?.isAdmin ?? false,
userAttributes: userAttributes
)
.variant(for: OptimizelyExperiment.Key.nativeOnboarding)

switch optimizelyVariant {
case .variant1, .variant2:
Expand Down
31 changes: 20 additions & 11 deletions Library/OptimizelyClientType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ public protocol OptimizelyClientType: AnyObject {
extension OptimizelyClientType {
public func variant(
for experiment: OptimizelyExperiment.Key,
userId: String,
isAdmin: Bool,
userAttributes: [String: Any]? = nil
userAttributes: [String: Any]? = optimizelyUserAttributes()
) -> OptimizelyExperiment.Variant {
let variationString: String?

let userId = deviceIdentifier(uuid: UUID())
let isAdmin = AppEnvironment.current.currentUser?.isAdmin ?? false

if isAdmin {
variationString = try? self.getVariationKey(
experimentKey: experiment.rawValue, userId: userId, attributes: userAttributes
Expand All @@ -38,12 +39,13 @@ extension OptimizelyClientType {
}
}

// MARK: - Tracking Properties

public func optimizelyTrackingAttributesAndEventTags(
with user: User?,
project: Project?,
refTag: RefTag?
with project: Project? = nil,
refTag: RefTag? = nil
) -> ([String: Any], [String: Any]) {
let properties = optimizelyUserAttributes(with: user, project: project, refTag: refTag)
let properties = optimizelyUserAttributes(with: project, refTag: refTag)

let eventTags: [String: Any] = ([
"project_subcategory": project?.category.name,
Expand All @@ -56,30 +58,37 @@ public func optimizelyTrackingAttributesAndEventTags(
}

public func optimizelyUserAttributes(
with user: User? = nil,
project: Project? = nil,
with project: Project? = nil,
refTag: RefTag? = nil
) -> [String: Any] {
let user = AppEnvironment.current.currentUser

let properties: [String: Any] = [
"user_distinct_id": debugAdminDeviceIdentifier(),
"user_backed_projects_count": user?.stats.backedProjectsCount,
"user_launched_projects_count": user?.stats.createdProjectsCount,
"user_country": (user?.location?.country ?? AppEnvironment.current.config?.countryCode)?.lowercased(),
"user_facebook_account": user?.facebookConnected,
"user_display_language": AppEnvironment.current.language.rawValue,
"session_ref_tag": refTag?.stringTag,
"session_referrer_credit": project.flatMap(cookieRefTagFor(project:)).coalesceWith(refTag)?.stringTag,
"session_os_version": AppEnvironment.current.device.systemVersion,
"session_user_is_logged_in": user != nil,
"session_app_release_version": AppEnvironment.current.mainBundle.shortVersionString,
"session_apple_pay_device": AppEnvironment.current.applePayCapabilities.applePayDevice(),
"session_device_format": AppEnvironment.current.device.deviceFormat
]
.compact()
.withAllValuesFrom(sessionRefTagProperties(with: project, refTag: refTag))

return properties
}

private func sessionRefTagProperties(with project: Project?, refTag: RefTag?) -> [String: Any] {
return ([
"session_referrer_credit": project.flatMap(cookieRefTagFor(project:)).coalesceWith(refTag)?.stringTag,
"session_ref_tag": refTag?.stringTag
] as [String: Any?]).compact()
}

private func debugAdminDeviceIdentifier() -> String? {
guard
AppEnvironment.current.environmentType != .production,
Expand Down
27 changes: 16 additions & 11 deletions Library/OptimizelyClientTypeTests.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@testable import KsApi
@testable import Library
import Prelude
import XCTest
Expand All @@ -10,7 +11,7 @@ final class OptimizelyClientTypeTests: TestCase {

XCTAssertEqual(
OptimizelyExperiment.Variant.variant1,
mockClient.variant(for: .pledgeCTACopy, userId: "123", isAdmin: false),
mockClient.variant(for: .pledgeCTACopy),
"Returns the correction variation"
)
XCTAssertTrue(mockClient.activatePathCalled)
Expand All @@ -25,7 +26,7 @@ final class OptimizelyClientTypeTests: TestCase {

XCTAssertEqual(
OptimizelyExperiment.Variant.control,
mockClient.variant(for: .pledgeCTACopy, userId: "123", isAdmin: false),
mockClient.variant(for: .pledgeCTACopy),
"Returns the control variant if error is thrown"
)
XCTAssertTrue(mockClient.activatePathCalled)
Expand All @@ -37,7 +38,7 @@ final class OptimizelyClientTypeTests: TestCase {

XCTAssertEqual(
OptimizelyExperiment.Variant.control,
mockClient.variant(for: .pledgeCTACopy, userId: "123", isAdmin: false),
mockClient.variant(for: .pledgeCTACopy),
"Returns the control variant if experiment key is not found"
)
XCTAssertTrue(mockClient.activatePathCalled)
Expand All @@ -51,7 +52,7 @@ final class OptimizelyClientTypeTests: TestCase {

XCTAssertEqual(
OptimizelyExperiment.Variant.control,
mockClient.variant(for: .pledgeCTACopy, userId: "123", isAdmin: false),
mockClient.variant(for: .pledgeCTACopy),
"Returns the control variant if the variant is not recognized"
)
XCTAssertTrue(mockClient.activatePathCalled)
Expand All @@ -63,12 +64,16 @@ final class OptimizelyClientTypeTests: TestCase {
|> \.experiments .~
[OptimizelyExperiment.Key.pledgeCTACopy.rawValue: OptimizelyExperiment.Variant.variant1.rawValue]

XCTAssertEqual(
OptimizelyExperiment.Variant.variant1,
mockClient.variant(for: .pledgeCTACopy, userId: "123", isAdmin: true),
"Returns the correction variation"
)
XCTAssertFalse(mockClient.activatePathCalled)
XCTAssertTrue(mockClient.getVariantPathCalled)
let user = User.template |> User.lens.isAdmin .~ true

withEnvironment(currentUser: user) {
XCTAssertEqual(
OptimizelyExperiment.Variant.variant1,
mockClient.variant(for: .pledgeCTACopy),
"Returns the correction variation"
)
XCTAssertFalse(mockClient.activatePathCalled)
XCTAssertTrue(mockClient.getVariantPathCalled)
}
}
}
14 changes: 2 additions & 12 deletions Library/OptimizelyExperiment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,10 @@ extension OptimizelyExperiment {
project: Project,
refTag: RefTag?
) -> OptimizelyExperiment.Variant? {
let userAttributes = optimizelyUserAttributes(
with: AppEnvironment.current.currentUser,
project: project,
refTag: refTag
)

let optimizelyVariant = AppEnvironment.current.optimizelyClient?
return AppEnvironment.current.optimizelyClient?
.variant(
for: OptimizelyExperiment.Key.nativeProjectPageCampaignDetails,
userId: deviceIdentifier(uuid: UUID()),
isAdmin: AppEnvironment.current.currentUser?.isAdmin ?? false,
userAttributes: userAttributes
userAttributes: optimizelyUserAttributes(with: project, refTag: refTag)
)

return optimizelyVariant
}
}
19 changes: 2 additions & 17 deletions Library/ViewModels/LandingPageViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,7 @@ public final class LandingPageViewModel: LandingPageViewModelType, LandingPageVi

self.ctaButtonTappedSignal
.observeValues {
let (properties, eventTags) = optimizelyTrackingAttributesAndEventTags(
with: AppEnvironment.current.currentUser,
project: nil,
refTag: nil
)
let (properties, eventTags) = optimizelyTrackingAttributesAndEventTags()

try? AppEnvironment.current.optimizelyClient?
.track(
Expand Down Expand Up @@ -73,19 +69,8 @@ public final class LandingPageViewModel: LandingPageViewModelType, LandingPageVi
}

private func cards() -> [LandingPageCardType]? {
let userAttributes = optimizelyUserAttributes(
with: AppEnvironment.current.currentUser,
project: nil,
refTag: nil
)

let optimizelyVariant = AppEnvironment.current.optimizelyClient?
.variant(
for: OptimizelyExperiment.Key.nativeOnboarding,
userId: deviceIdentifier(uuid: UUID()),
isAdmin: AppEnvironment.current.currentUser?.isAdmin ?? false,
userAttributes: userAttributes
)
.variant(for: OptimizelyExperiment.Key.nativeOnboarding)

guard let variant = optimizelyVariant else {
return nil
Expand Down
10 changes: 1 addition & 9 deletions Library/ViewModels/PledgeCTAContainerViewViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,10 @@ private func pledgeCTA(project: Project, refTag: RefTag?, backing: Backing?) ->
return PledgeStateCTAType.viewYourRewards
}

let userAttributes = optimizelyUserAttributes(
with: AppEnvironment.current.currentUser,
project: project,
refTag: refTag
)

let optimizelyVariant = AppEnvironment.current.optimizelyClient?
.variant(
for: OptimizelyExperiment.Key.pledgeCTACopy,
userId: deviceIdentifier(uuid: UUID()),
isAdmin: AppEnvironment.current.currentUser?.isAdmin ?? false,
userAttributes: userAttributes
userAttributes: optimizelyUserAttributes(with: project, refTag: refTag)
)

if let variant = optimizelyVariant, project.state == .live {
Expand Down
46 changes: 44 additions & 2 deletions Library/ViewModels/PledgeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -599,8 +599,7 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
initialData
.observeValues { project, _, refTag, _ in
let (properties, eventTags) = optimizelyTrackingAttributesAndEventTags(
with: AppEnvironment.current.currentUser,
project: project,
with: project,
refTag: refTag
)

Expand Down Expand Up @@ -628,6 +627,25 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
refTag: refTag
)
}

createBackingDataAndIsApplePay.takeWhen(createBackingCompletionEvents)
.observeValues { data, isApplePay in
let (properties, eventTags) = optimizelyTrackingAttributesAndEventTags(
with: data.project,
refTag: data.refTag
)

let allEventTags = eventTags
.withAllValuesFrom(optimizelyCheckoutEventTags(createBackingData: data, isApplePay: isApplePay))

try? AppEnvironment.current.optimizelyClient?
.track(
eventKey: "App Completed Checkout",
userId: deviceIdentifier(uuid: UUID()),
attributes: properties,
eventTags: allEventTags
)
}
}

// MARK: - Inputs
Expand Down Expand Up @@ -892,3 +910,27 @@ private func checkoutPropertiesData(
userHasStoredApplePayCard: userHasEligibleStoredApplePayCard
)
}

private func optimizelyCheckoutEventTags(
createBackingData: CreateBackingData,
isApplePay: Bool
) -> [String: Any] {
let pledgeTotal = createBackingData.pledgeAmount

let pledgeTotalUsdCents = pledgeTotal
.multiplyingCurrency(Double(createBackingData.project.stats.staticUsdRate))
.multiplyingCurrency(100.0)
.rounded()

let paymentType = isApplePay
? Backing.PaymentType.applePay.rawValue
: Backing.PaymentType.creditCard.rawValue

return [
"checkout_amount": pledgeTotal,
"checkout_payment_type": paymentType,
"revenue": Int(pledgeTotalUsdCents),
"currency": createBackingData.project.stats.currency,
"category": createBackingData.project.category.name
]
}
Loading

0 comments on commit 4bdc931

Please # to comment.