Skip to content

Commit a54cd3e

Browse files
authored
[Woo POS][Barcodes] Analytics tracking for barcode scanning (#15758)
2 parents 12c61cd + cbc63cd commit a54cd3e

File tree

6 files changed

+84
-23
lines changed

6 files changed

+84
-23
lines changed

WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSale.swift

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,29 @@ extension WooAnalyticsEvent {
3737
}
3838

3939
static func addItemToCart(
40-
sourceView: WooAnalyticsEvent.PointOfSale.SourceView,
40+
sourceView: WooAnalyticsEvent.PointOfSale.SourceView? = nil,
4141
sourceViewType: WooAnalyticsEvent.PointOfSale.SourceViewType,
4242
itemType: WooAnalyticsEvent.PointOfSale.ItemType,
43-
productType: WooAnalyticsEvent.PointOfSale.CartItemProductType? = nil
43+
productType: WooAnalyticsEvent.PointOfSale.CartItemProductType? = nil,
44+
error: Error? = nil
4445
) -> WooAnalyticsEvent {
4546
var properties: [String: String] = [
46-
Key.sourceView: sourceView.rawValue,
4747
Key.sourceViewType: sourceViewType.rawValue,
4848
Key.itemType: itemType.rawValue
4949
]
5050

51+
if let sourceView {
52+
properties[Key.sourceView] = sourceView.rawValue
53+
}
54+
5155
if let productType {
5256
properties[Key.productType] = productType.rawValue
5357
}
5458

5559
return WooAnalyticsEvent(
5660
statName: .pointOfSaleAddItemToCart,
57-
properties: properties
61+
properties: properties,
62+
error: error
5863
)
5964
}
6065

@@ -217,6 +222,7 @@ extension WooAnalyticsEvent.PointOfSale {
217222
case list
218223
case search
219224
case preSearch = "pre_search"
225+
case scanner
220226

221227
init(isSearching: Bool, searchTerm: String = "") {
222228
switch (isSearching, searchTerm.isEmpty) {
@@ -235,6 +241,19 @@ extension WooAnalyticsEvent.PointOfSale {
235241
enum ItemType: String {
236242
case product
237243
case coupon
244+
case loading
245+
case error
246+
247+
init(cartItem: Cart.PurchasableItem) {
248+
switch cartItem.state {
249+
case .loaded:
250+
self = .product
251+
case .loading:
252+
self = .loading
253+
case .error:
254+
self = .error
255+
}
256+
}
238257
}
239258

240259
/// Types of products supported in the POS

WooCommerce/Classes/POS/Models/Cart+BarcodeScanError.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import Foundation
22
import enum Yosemite.PointOfSaleBarcodeScanError
33

44
extension Cart {
5-
mutating func updateLoadingItem(id: UUID, with error: PointOfSaleBarcodeScanError) {
6-
guard let index = purchasableItems.firstIndex(where: { $0.id == id }) else { return }
5+
@discardableResult
6+
mutating func updateLoadingItem(id: UUID, with error: PointOfSaleBarcodeScanError) -> Cart.PurchasableItem? {
7+
guard let index = purchasableItems.firstIndex(where: { $0.id == id }) else { return nil }
78

89
purchasableItems[index] = Cart.PurchasableItem(
910
id: id,
@@ -12,6 +13,8 @@ extension Cart {
1213
quantity: 1,
1314
state: .error
1415
)
16+
17+
return purchasableItems[index]
1518
}
1619

1720
private func title(for error: PointOfSaleBarcodeScanError) -> String {
@@ -30,7 +33,13 @@ extension Cart {
3033
}
3134

3235
private func subtitle(for error: PointOfSaleBarcodeScanError) -> String {
33-
switch error {
36+
return error.localizedDescription
37+
}
38+
}
39+
40+
extension PointOfSaleBarcodeScanError {
41+
var localizedDescription: String {
42+
switch self {
3443
case .notFound, .unknown:
3544
return Localization.notFound
3645
case .downloadableProduct, .unsupportedProductType:
@@ -45,10 +54,8 @@ extension Cart {
4554
}
4655
}
4756
}
48-
}
4957

50-
private extension Cart {
51-
enum Localization {
58+
private enum Localization {
5259
static let notFound = NSLocalizedString(
5360
"pointOfSale.barcodeScan.error.notFound",
5461
value: "Unknown scanned item",

WooCommerce/Classes/POS/Models/Cart.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,23 @@ extension Cart {
100100
}
101101
}
102102

103-
mutating func addLoadingItem() -> UUID {
103+
mutating func addLoadingItem() -> Cart.PurchasableItem {
104104
let id = UUID()
105105
let loadingItem = PurchasableItem.loading(id: id)
106106
purchasableItems.insert(loadingItem, at: purchasableItems.startIndex)
107-
return id
107+
return loadingItem
108108
}
109109

110-
mutating func updateLoadingItem(id: UUID, with posItem: POSItem) {
111-
guard let index = purchasableItems.firstIndex(where: { $0.id == id }) else { return }
110+
@discardableResult
111+
mutating func updateLoadingItem(id: UUID, with posItem: POSItem) -> Cart.PurchasableItem? {
112+
guard let index = purchasableItems.firstIndex(where: { $0.id == id }) else { return nil }
112113

113114
if let productItem = createPurchasableItem(id: id, from: posItem) {
114115
purchasableItems[index] = productItem
116+
return productItem
115117
} else {
116118
purchasableItems.remove(at: index)
119+
return nil
117120
}
118121
}
119122

WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,40 @@ extension PointOfSaleAggregateModel {
187187
extension PointOfSaleAggregateModel {
188188
func barcodeScanned(_ barcode: String) {
189189
Task {
190-
let placeholderItemID = cart.addLoadingItem()
190+
let placeholderItemID = cart.addLoadingItem().id
191+
192+
analytics.track(
193+
event: .PointOfSale.addItemToCart(
194+
sourceViewType: .scanner,
195+
itemType: .loading
196+
)
197+
)
198+
191199
do throws(PointOfSaleBarcodeScanError) {
192200
let item = try await barcodeScanService.getItem(barcode: barcode)
193-
cart.updateLoadingItem(id: placeholderItemID, with: item)
201+
if let cartItem = cart.updateLoadingItem(id: placeholderItemID, with: item) {
202+
analytics.track(
203+
event: .PointOfSale.addItemToCart(
204+
sourceViewType: .scanner,
205+
itemType: .product,
206+
productType: .init(cartItem: cartItem)
207+
)
208+
)
209+
}
194210
} catch {
195211
DDLogInfo("Failed to find item by barcode: \(error)")
196-
cart.updateLoadingItem(id: placeholderItemID, with: error)
197-
await soundPlayer.playSound(.barcodeScanFailure)
212+
if let _ = cart.updateLoadingItem(id: placeholderItemID, with: error) {
213+
// Only play a sound and track analytics if the item still exists in the cart.
214+
await soundPlayer.playSound(.barcodeScanFailure)
215+
216+
analytics.track(
217+
event: .PointOfSale.addItemToCart(
218+
sourceViewType: .scanner,
219+
itemType: .error,
220+
error: error
221+
)
222+
)
223+
}
198224
}
199225
}
200226
}

WooCommerce/Classes/POS/Presentation/CartView.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,19 @@ struct CartView: View {
7373
ServiceLocator.analytics.track(
7474
event: .PointOfSale.itemRemovedFromCart(
7575
sourceView: .cart,
76-
itemType: .product,
76+
itemType: .init(cartItem: cartItem),
7777
productType: .init(cartItem: cartItem)
7878
)
7979
)
8080
posModel.remove(cartItem: cartItem)
8181
} : nil,
8282
onCancelLoading: {
83+
ServiceLocator.analytics.track(
84+
event: .PointOfSale.itemRemovedFromCart(
85+
sourceView: .cart,
86+
itemType: .loading
87+
)
88+
)
8389
posModel.cancelLoadingItem(id: cartItem.id)
8490
})
8591
.id(cartItem.id)

WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,12 @@ struct PointOfSaleAggregateModelTests {
125125
try #require(cart.purchasableItems.isEmpty)
126126

127127
// When
128-
let id = cart.addLoadingItem()
128+
let loadingItem = cart.addLoadingItem()
129129

130130
// Then
131131
#expect(cart.purchasableItems.count == 1)
132132
let item = try #require(cart.purchasableItems.first)
133-
#expect(item.id == id)
133+
#expect(item.id == loadingItem.id)
134134
guard case .loading = item.state else {
135135
throw CartTestError.unexpectedItemStateInCart
136136
}
@@ -140,11 +140,11 @@ struct PointOfSaleAggregateModelTests {
140140
@Test func updateLoadingItem_updates_loading_item_with_simple_product() async throws {
141141
// Given
142142
var cart = Cart()
143-
let id = cart.addLoadingItem()
143+
let loadingItem = cart.addLoadingItem()
144144
let purchasableItem = makePurchasableItem(name: "Test Product")
145145

146146
// When
147-
cart.updateLoadingItem(id: id, with: purchasableItem)
147+
cart.updateLoadingItem(id: loadingItem.id, with: purchasableItem)
148148

149149
// Then
150150
#expect(cart.purchasableItems.count == 1)

0 commit comments

Comments
 (0)