Skip to content

Commit 003e1f3

Browse files
authored
fix(Analytics): Updating session stop time for cached events. (#3405)
1 parent 0d8f12a commit 003e1f3

File tree

11 files changed

+187
-139
lines changed

11 files changed

+187
-139
lines changed

AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/AnalyticsClient.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public protocol AnalyticsClientBehaviour: Actor {
3131
onSubmit: SubmitResult?)
3232

3333
@discardableResult func submitEvents() async throws -> [PinpointEvent]
34+
func update(_ session: PinpointSession) async throws
3435

3536
nonisolated func createAppleMonetizationEvent(with transaction: SKPaymentTransaction,
3637
with product: SKProduct) -> PinpointEvent
@@ -262,6 +263,10 @@ actor AnalyticsClient: AnalyticsClientBehaviour {
262263
func submitEvents() async throws -> [PinpointEvent] {
263264
return try await eventRecorder.submitAllEvents()
264265
}
266+
267+
func update(_ session: PinpointSession) async throws {
268+
try await eventRecorder.update(session)
269+
}
265270

266271
func setAutomaticSubmitEventsInterval(_ interval: TimeInterval,
267272
onSubmit: SubmitResult?) {

AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/EventRecorder.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ protocol AnalyticsEventRecording: Actor {
2828
func updateAttributesOfEvents(ofType: String,
2929
withSessionId: PinpointSession.SessionId,
3030
setAttributes: [String: String]) throws
31+
32+
/// Updates the session information of the events that match the same sessionId.
33+
/// - Parameter session: The session to update
34+
func update(_ session: PinpointSession) throws
3135

3236
/// Submit all locally stored events
3337
/// - Returns: A collection of events submitted to Pinpoint
@@ -78,6 +82,10 @@ actor EventRecorder: AnalyticsEventRecording {
7882
setAttributes: attributes)
7983
}
8084

85+
func update(_ session: PinpointSession) throws {
86+
try storage.updateSession(session)
87+
}
88+
8189
/// Submit all locally stored events in batches. If a previous submission is in progress, it waits until it's completed before proceeding.
8290
/// When the submission for an event is accepted, the event is removed from local storage
8391
/// When the submission for an event is rejected, the event retry count is incremented in the local storage. Events that exceed the maximum retry count (3) are purged.

AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/LocalStorage/AnalyticsEventSQLStorage.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,22 @@ class AnalyticsEventSQLStorage: AnalyticsEventStorage {
115115
sessionId,
116116
eventType])
117117
}
118+
119+
func updateSession(_ session: PinpointSession) throws {
120+
let updateStatement = """
121+
UPDATE Event
122+
SET sessionStartTime = ?, sessionStopTime = ?
123+
WHERE sessionId = ?
124+
"""
125+
_ = try dbAdapter.executeQuery(
126+
updateStatement,
127+
[
128+
session.startTime.asISO8601String,
129+
session.stopTime?.asISO8601String,
130+
session.sessionId
131+
]
132+
)
133+
}
118134

119135
/// Get the oldest event with limit
120136
/// - Parameter limit: The number of query result to limit

AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/LocalStorage/AnalyticsEventStorage.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ protocol AnalyticsEventStorage {
2929
func updateEvents(ofType: String,
3030
withSessionId: PinpointSession.SessionId,
3131
setAttributes: [String: String]) throws
32+
33+
/// Updates the session information of the events that match the same sessionId.
34+
/// - Parameter session: The session to update
35+
func updateSession(_ session: PinpointSession) throws
3236

3337
/// Get the oldest event with limit
3438
/// - Parameter limit: The number of query result to limit

AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/PinpointEvent+PinpointClientTypes.swift

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,36 @@ import AWSPluginsCore
1010
import Foundation
1111

1212
extension PinpointEvent {
13-
var clientTypeSession: PinpointClientTypes.Session? {
14-
#if os(watchOS)
15-
// If the session duration cannot be represented by Int, return a nil session instead.
16-
// This is extremely unlikely to happen since a session's stopTime is set when the app is closed
17-
if let duration = session.duration, duration > Int.max {
18-
return nil
13+
private var clientTypeSession: PinpointClientTypes.Session? {
14+
var sessionDuration: Int? = nil
15+
if let duration = session.duration {
16+
// If the session duration cannot be represented by Int, return a nil session instead.
17+
// This is extremely unlikely to happen since a session's stopTime is set when the app is closed
18+
guard let intDuration = Int(exactly: duration) else { return nil }
19+
sessionDuration = intDuration
1920
}
20-
#endif
21-
return PinpointClientTypes.Session(duration: Int(session.duration),
22-
id: session.sessionId,
23-
startTimestamp: session.startTime.asISO8601String,
24-
stopTimestamp: session.stopTime?.asISO8601String)
21+
22+
return .init(
23+
duration: sessionDuration,
24+
id: session.sessionId,
25+
startTimestamp: session.startTime.asISO8601String,
26+
stopTimestamp: session.stopTime?.asISO8601String
27+
)
2528
}
2629

2730
var clientTypeEvent: PinpointClientTypes.Event {
28-
return PinpointClientTypes.Event(appPackageName: Bundle.main.appPackageName,
29-
appTitle: Bundle.main.appName,
30-
appVersionCode: Bundle.main.appVersion,
31-
attributes: attributes,
32-
clientSdkVersion: AmplifyAWSServiceConfiguration.amplifyVersion,
33-
eventType: eventType,
34-
metrics: metrics,
35-
sdkName: AmplifyAWSServiceConfiguration.platformName,
36-
session: clientTypeSession,
37-
timestamp: eventDate.asISO8601String)
31+
return .init(
32+
appPackageName: Bundle.main.appPackageName,
33+
appTitle: Bundle.main.appName,
34+
appVersionCode: Bundle.main.appVersion,
35+
attributes: attributes,
36+
clientSdkVersion: AmplifyAWSServiceConfiguration.amplifyVersion,
37+
eventType: eventType,
38+
metrics: metrics,
39+
sdkName: AmplifyAWSServiceConfiguration.platformName,
40+
session: clientTypeSession,
41+
timestamp: eventDate.asISO8601String
42+
)
3843
}
3944
}
4045

@@ -55,12 +60,3 @@ extension Bundle {
5560
object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
5661
}
5762
}
58-
59-
private extension Int {
60-
init?(_ value: Int64?) {
61-
guard let value = value else {
62-
return nil
63-
}
64-
self.init(value)
65-
}
66-
}

AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Session/PinpointSession.swift

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,53 +8,79 @@
88
import Foundation
99

1010
@_spi(InternalAWSPinpoint)
11-
public class PinpointSession: Codable {
11+
public struct PinpointSession: Codable {
12+
private enum State: Codable {
13+
case active
14+
case paused(date: Date)
15+
case stopped(date: Date)
16+
}
1217
typealias SessionId = String
1318

1419
let sessionId: SessionId
1520
let startTime: Date
16-
private(set) var stopTime: Date?
21+
var stopTime: Date? {
22+
switch state {
23+
case .active:
24+
return nil
25+
case .paused(let stopTime),
26+
.stopped(let stopTime):
27+
return stopTime
28+
}
29+
}
30+
31+
private var state: State = .active
1732

1833
init(appId: String,
1934
uniqueId: String) {
2035
sessionId = Self.generateSessionId(appId: appId,
2136
uniqueId: uniqueId)
2237
startTime = Date()
23-
stopTime = nil
2438
}
2539

2640
init(sessionId: SessionId,
2741
startTime: Date,
2842
stopTime: Date?) {
2943
self.sessionId = sessionId
3044
self.startTime = startTime
31-
self.stopTime = stopTime
45+
if let stopTime {
46+
self.state = .stopped(date: stopTime)
47+
}
3248
}
3349

3450
var isPaused: Bool {
35-
return stopTime != nil
51+
if case .paused = state {
52+
return true
53+
}
54+
55+
return false
56+
}
57+
58+
var isStopped: Bool {
59+
if case .stopped = state {
60+
return true
61+
}
62+
63+
return false
3664
}
3765

3866
var duration: Date.Millisecond? {
3967
/// According to Pinpoint's documentation, `duration` is only required if `stopTime` is not nil.
40-
guard let endTime = stopTime else {
41-
return nil
42-
}
43-
return endTime.millisecondsSince1970 - startTime.millisecondsSince1970
68+
guard let stopTime else { return nil }
69+
return stopTime.millisecondsSince1970 - startTime.millisecondsSince1970
4470
}
4571

46-
func stop() {
47-
guard stopTime == nil else { return }
48-
stopTime = Date()
72+
mutating func stop() {
73+
guard !isStopped else { return }
74+
state = .stopped(date: stopTime ?? Date())
4975
}
5076

51-
func pause() {
77+
mutating func pause() {
5278
guard !isPaused else { return }
53-
stopTime = Date()
79+
state = .paused(date: Date())
5480
}
5581

56-
func resume() {
57-
stopTime = nil
82+
mutating func resume() {
83+
state = .active
5884
}
5985

6086
private static func generateSessionId(appId: String,

0 commit comments

Comments
 (0)