Skip to content

Commit 99bde2c

Browse files
authored
[Woo POS][Local Catalog] Beta-fix Add Better Analytics to Background Task Refresh System (#16014)
2 parents 127c98a + 7758a92 commit 99bde2c

File tree

3 files changed

+148
-4
lines changed

3 files changed

+148
-4
lines changed

WooCommerce/Classes/Analytics/WooAnalyticsEvent+BackgroudUpdates.swift

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,51 @@
11
import Foundation
2+
import WooFoundation
23

34
extension WooAnalyticsEvent {
45
enum BackgroundUpdates {
56

67
private enum Keys {
78
static let timeTaken = "time_taken"
9+
static let backgroundTimeGranted = "background_time_granted"
10+
static let networkType = "network_type"
11+
static let isExpensiveConnection = "is_expensive_connection"
12+
static let isLowDataMode = "is_low_data_mode"
13+
static let isPowered = "is_powered"
14+
static let batteryLevel = "battery_level"
15+
static let isLowPowerMode = "is_low_power_mode"
16+
static let timeSinceLastRun = "time_since_last_run"
817
}
918

10-
static func dataSynced(timeTaken: TimeInterval) -> WooAnalyticsEvent {
11-
WooAnalyticsEvent(statName: .backgroundDataSynced, properties: [Keys.timeTaken: timeTaken])
19+
static func dataSynced(
20+
timeTaken: TimeInterval,
21+
backgroundTimeGranted: TimeInterval?,
22+
networkType: String,
23+
isExpensiveConnection: Bool,
24+
isLowDataMode: Bool,
25+
isPowered: Bool,
26+
batteryLevel: Float,
27+
isLowPowerMode: Bool,
28+
timeSinceLastRun: TimeInterval?
29+
) -> WooAnalyticsEvent {
30+
var properties: [String: WooAnalyticsEventPropertyType] = [
31+
Keys.timeTaken: Int64(timeTaken),
32+
Keys.networkType: networkType,
33+
Keys.isExpensiveConnection: isExpensiveConnection,
34+
Keys.isLowDataMode: isLowDataMode,
35+
Keys.isPowered: isPowered,
36+
Keys.batteryLevel: Float64(batteryLevel),
37+
Keys.isLowPowerMode: isLowPowerMode
38+
]
39+
40+
if let backgroundTimeGranted = backgroundTimeGranted {
41+
properties[Keys.backgroundTimeGranted] = Int64(backgroundTimeGranted)
42+
}
43+
44+
if let timeSinceLastRun = timeSinceLastRun {
45+
properties[Keys.timeSinceLastRun] = Int64(timeSinceLastRun)
46+
}
47+
48+
return WooAnalyticsEvent(statName: .backgroundDataSynced, properties: properties)
1249
}
1350

1451
static func dataSyncError(_ error: Error) -> WooAnalyticsEvent {

WooCommerce/Classes/Extensions/UserDefaults+Woo.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ extension UserDefaults {
5050

5151
// Background Task Refresh
5252
case latestBackgroundOrderSyncDate
53+
case lastBackgroundRefreshCompletionTime
5354

5455
// Blaze Local notification
5556
case blazeNoCampaignReminderOpened

WooCommerce/Classes/Tools/BackgroundTasks/BackgroundTaskRefreshDispatcher.swift

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import UIKit
22
import Foundation
33
import BackgroundTasks
4+
import Network
45

56
final class BackgroundTaskRefreshDispatcher {
67

@@ -60,6 +61,7 @@ final class BackgroundTaskRefreshDispatcher {
6061
// Launch all refresh tasks in parallel.
6162
let refreshTasks = Task {
6263
do {
64+
async let systemInfo = BackgroundTaskSystemInfo()
6365

6466
let startTime = Date.now
6567

@@ -78,7 +80,27 @@ final class BackgroundTaskRefreshDispatcher {
7880
}
7981

8082
let timeTaken = round(Date.now.timeIntervalSince(startTime))
81-
ServiceLocator.analytics.track(event: .BackgroundUpdates.dataSynced(timeTaken: timeTaken))
83+
84+
var timeSinceLastRun: TimeInterval? = nil
85+
if let lastRunTime = UserDefaults.standard[.lastBackgroundRefreshCompletionTime] as? Date {
86+
timeSinceLastRun = round(lastRunTime.timeIntervalSinceNow.magnitude)
87+
}
88+
89+
await ServiceLocator.analytics.track(event: .BackgroundUpdates.dataSynced(
90+
timeTaken: timeTaken,
91+
backgroundTimeGranted: systemInfo.backgroundTimeGranted,
92+
networkType: systemInfo.networkType,
93+
isExpensiveConnection: systemInfo.isExpensiveConnection,
94+
isLowDataMode: systemInfo.isLowDataMode,
95+
isPowered: systemInfo.isPowered,
96+
batteryLevel: systemInfo.batteryLevel,
97+
isLowPowerMode: systemInfo.isLowPowerMode,
98+
timeSinceLastRun: timeSinceLastRun
99+
))
100+
101+
// Save date, for use in analytics next time we refresh
102+
UserDefaults.standard[.lastBackgroundRefreshCompletionTime] = Date.now
103+
82104
backgroundTask.setTaskCompleted(success: true)
83105

84106
} catch {
@@ -93,7 +115,7 @@ final class BackgroundTaskRefreshDispatcher {
93115
ServiceLocator.analytics.track(event: .BackgroundUpdates.dataSyncError(BackgroundError.expired))
94116
refreshTasks.cancel()
95117
}
96-
}
118+
}
97119
}
98120

99121
private extension BackgroundTaskRefreshDispatcher {
@@ -109,3 +131,87 @@ extension BackgroundTaskRefreshDispatcher {
109131
case expired
110132
}
111133
}
134+
135+
// MARK: - System Information Helper
136+
137+
private struct NetworkInfo {
138+
let type: String
139+
let isExpensive: Bool
140+
let isLowDataMode: Bool
141+
}
142+
143+
private struct BackgroundTaskSystemInfo {
144+
let backgroundTimeGranted: TimeInterval?
145+
private let networkInfo: NetworkInfo
146+
let isPowered: Bool
147+
let batteryLevel: Float
148+
let isLowPowerMode: Bool
149+
150+
// Computed properties for clean external access
151+
var networkType: String { networkInfo.type }
152+
var isExpensiveConnection: Bool { networkInfo.isExpensive }
153+
var isLowDataMode: Bool { networkInfo.isLowDataMode }
154+
155+
@MainActor
156+
init() async {
157+
// Background time granted (nil if foreground/unlimited)
158+
let backgroundTime = UIApplication.shared.backgroundTimeRemaining
159+
self.backgroundTimeGranted = backgroundTime < Double.greatestFiniteMagnitude ? backgroundTime : nil
160+
161+
// Network info
162+
self.networkInfo = await Self.getNetworkInfo()
163+
164+
// Power and battery info
165+
let device = UIDevice.current
166+
device.isBatteryMonitoringEnabled = true
167+
168+
self.isPowered = device.batteryState == .charging || device.batteryState == .full
169+
self.batteryLevel = device.batteryLevel
170+
self.isLowPowerMode = ProcessInfo.processInfo.isLowPowerModeEnabled
171+
172+
device.isBatteryMonitoringEnabled = false
173+
}
174+
175+
private static func getNetworkInfo() async -> NetworkInfo {
176+
return await withCheckedContinuation { continuation in
177+
let monitor = NWPathMonitor()
178+
179+
monitor.pathUpdateHandler = { path in
180+
continuation.resume(returning: NetworkInfo(path: path))
181+
monitor.cancel()
182+
}
183+
184+
let queue = DispatchQueue(label: "network.monitor.queue")
185+
monitor.start(queue: queue)
186+
}
187+
}
188+
}
189+
190+
private extension NetworkInfo {
191+
init(path: NWPath) {
192+
guard path.status == .satisfied else {
193+
self.type = "no_connection"
194+
self.isExpensive = false
195+
self.isLowDataMode = false
196+
return
197+
}
198+
199+
self.type = Self.networkType(from: path)
200+
self.isExpensive = path.isExpensive
201+
self.isLowDataMode = path.isConstrained
202+
}
203+
204+
private static func networkType(from path: NWPath) -> String {
205+
if path.usesInterfaceType(.wifi) {
206+
return "wifi"
207+
} else if path.usesInterfaceType(.cellular) {
208+
return "cellular"
209+
} else if path.usesInterfaceType(.wiredEthernet) {
210+
return "ethernet"
211+
} else if path.usesInterfaceType(.loopback) {
212+
return "loopback"
213+
} else {
214+
return "other"
215+
}
216+
}
217+
}

0 commit comments

Comments
 (0)