Skip to content

Commit fe411fe

Browse files
zhu-xiaoweixiaoweii
andauthored
feat: add _app_start event, change user engagement attribute (#17)
Co-authored-by: xiaoweii <xiaoweii@amazom.com>
1 parent a16901f commit fe411fe

File tree

6 files changed

+82
-29
lines changed

6 files changed

+82
-29
lines changed

Sources/Clickstream/Dependency/Clickstream/AutoRecord/AutoRecordEventClient.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ class AutoRecordEventClient {
1414
private let clickstream: ClickstreamContext
1515
private var isEntrances = false
1616
private var isFirstOpen: Bool
17+
private var isFirstTime = true
1718
private var startEngageTimestamp: Int64!
1819
private var lastScreenName: String?
1920
private var lastScreenPath: String?
21+
private var lastScreenStartTime: Int64?
2022

2123
init(clickstream: ClickstreamContext) {
2224
self.clickstream = clickstream
@@ -58,7 +60,7 @@ class AutoRecordEventClient {
5860
}
5961
}
6062

61-
func handleFirstOpen() {
63+
func handleAppStart() {
6264
checkAppVersionUpdate(clickstream: clickstream)
6365
checkOSVersionUpdate(clickstream: clickstream)
6466
if isFirstOpen {
@@ -67,13 +69,25 @@ class AutoRecordEventClient {
6769
UserDefaultsUtil.saveIsFirstOpen(storage: clickstream.storage, isFirstOpen: "false")
6870
isFirstOpen = false
6971
}
72+
let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.APP_START)
73+
event.addAttribute(isFirstTime, forKey: Event.ReservedAttribute.IS_FIRST_TIME)
74+
if lastScreenName != nil, lastScreenPath != nil {
75+
event.addAttribute(lastScreenName!, forKey: Event.ReservedAttribute.SCREEN_NAME)
76+
event.addAttribute(lastScreenPath!, forKey: Event.ReservedAttribute.SCREEN_ID)
77+
}
78+
recordEvent(event)
79+
isFirstTime = false
7080
}
7181

7282
func recordUserEngagement() {
7383
let engagementTime = Date().millisecondsSince1970 - startEngageTimestamp
7484
if engagementTime > Constants.minEngagementTime {
7585
let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.USER_ENGAGEMENT)
7686
event.addAttribute(engagementTime, forKey: Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP)
87+
if lastScreenName != nil, lastScreenPath != nil {
88+
event.addAttribute(lastScreenName!, forKey: Event.ReservedAttribute.SCREEN_NAME)
89+
event.addAttribute(lastScreenPath!, forKey: Event.ReservedAttribute.SCREEN_ID)
90+
}
7791
recordEvent(event)
7892
}
7993
}
@@ -95,6 +109,7 @@ class AutoRecordEventClient {
95109
if !clickstream.configuration.isTrackScreenViewEvents {
96110
return
97111
}
112+
let currentTimestamp = Date().millisecondsSince1970
98113
let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW)
99114
event.addAttribute(screenName, forKey: Event.ReservedAttribute.SCREEN_NAME)
100115
event.addAttribute(screenPath, forKey: Event.ReservedAttribute.SCREEN_ID)
@@ -103,13 +118,16 @@ class AutoRecordEventClient {
103118
event.addAttribute(lastScreenPath!, forKey: Event.ReservedAttribute.PREVIOUS_SCREEN_ID)
104119
}
105120
event.addAttribute(isEntrances ? 1 : 0, forKey: Event.ReservedAttribute.ENTRANCES)
106-
event.addAttribute(Date().millisecondsSince1970 - startEngageTimestamp,
107-
forKey: Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP)
121+
if !isEntrances, lastScreenStartTime != nil {
122+
event.addAttribute(currentTimestamp - lastScreenStartTime!,
123+
forKey: Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP)
124+
}
108125
recordEvent(event)
109126

110127
isEntrances = false
111128
lastScreenName = screenName
112129
lastScreenPath = screenPath
130+
lastScreenStartTime = currentTimestamp
113131
}
114132

115133
func recordEvent(_ event: ClickstreamEvent) {

Sources/Clickstream/Dependency/Clickstream/Event/Event.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ enum Event {
166166
static let PREVIOUS_SCREEN_NAME = "_previous_screen_name"
167167
static let SCREEN_ID = "_screen_id"
168168
static let SCREEN_NAME = "_screen_name"
169+
static let IS_FIRST_TIME = "_is_first_time"
169170
}
170171

171172
enum User {
@@ -207,6 +208,7 @@ enum Event {
207208
static let FIRST_OPEN = "_first_open"
208209
static let USER_ENGAGEMENT = "_user_engagement"
209210
static let SCREEN_VIEW = "_screen_view"
211+
static let APP_START = "_app_start"
210212
}
211213

212214
enum ErrorType {

Sources/Clickstream/Dependency/Clickstream/Session/SessionClient.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class SessionClient: SessionClientBehaviour {
5858
private func handleAppEnterForeground() {
5959
log.debug("Application entered the foreground.")
6060
autoRecordClient.updateEngageTimestamp()
61-
autoRecordClient.handleFirstOpen()
61+
autoRecordClient.handleAppStart()
6262
let isNewSession = initialSession()
6363
if isNewSession {
6464
autoRecordClient.setIsEntrances()

Tests/ClickstreamTests/Clickstream/AutoRecordEventClientTest.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class AutoRecordEventClientTest: XCTestCase {
8181
XCTAssertTrue(eventRecorder.lastSavedEvent?.eventType == Event.PresetEvent.SCREEN_VIEW)
8282
XCTAssertNotNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.SCREEN_ID])
8383
XCTAssertNotNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.SCREEN_NAME])
84-
XCTAssertNotNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP])
84+
XCTAssertNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP])
8585
XCTAssertNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.PREVIOUS_SCREEN_ID])
8686
XCTAssertNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.PREVIOUS_SCREEN_NAME])
8787
XCTAssertTrue(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.ENTRANCES] as! Int == 1)
@@ -104,6 +104,9 @@ class AutoRecordEventClientTest: XCTestCase {
104104
XCTAssertNotNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.SCREEN_ID])
105105
XCTAssertNotNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.SCREEN_NAME])
106106
XCTAssertNotNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP])
107+
XCTAssertTrue(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP]
108+
as! Int64 >= 0)
109+
107110
XCTAssertNotNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.PREVIOUS_SCREEN_ID])
108111
XCTAssertNotNil(eventRecorder.lastSavedEvent!.attributes[Event.ReservedAttribute.PREVIOUS_SCREEN_NAME])
109112

Tests/ClickstreamTests/Clickstream/SessionClientTests.swift

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,10 @@ class SessionClientTests: XCTestCase {
7575

7676
Thread.sleep(forTimeInterval: 0.1)
7777
let events = eventRecorder.savedEvents
78-
XCTAssertEqual(2, events.count)
78+
XCTAssertEqual(3, events.count)
7979
XCTAssertEqual(Event.PresetEvent.FIRST_OPEN, events[0].eventType)
80-
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[1].eventType)
80+
XCTAssertEqual(Event.PresetEvent.APP_START, events[1].eventType)
81+
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[2].eventType)
8182
}
8283

8384
func testGoBackground() {
@@ -91,9 +92,10 @@ class SessionClientTests: XCTestCase {
9192
XCTAssertTrue(storedSession != nil)
9293

9394
let events = eventRecorder.savedEvents
94-
XCTAssertEqual(2, events.count)
95+
XCTAssertEqual(3, events.count)
9596
XCTAssertEqual(Event.PresetEvent.FIRST_OPEN, events[0].eventType)
96-
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[1].eventType)
97+
XCTAssertEqual(Event.PresetEvent.APP_START, events[1].eventType)
98+
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[2].eventType)
9799
}
98100

99101
func testGoBackgroundWithUserEngagement() {
@@ -104,12 +106,13 @@ class SessionClientTests: XCTestCase {
104106
XCTAssertTrue(session.pauseTime != nil)
105107
let storedSession = UserDefaultsUtil.getSession(storage: clickstream.storage)
106108
XCTAssertTrue(storedSession != nil)
107-
109+
Thread.sleep(forTimeInterval: 0.1)
108110
let events = eventRecorder.savedEvents
109-
XCTAssertEqual(3, events.count)
111+
XCTAssertEqual(4, events.count)
110112
XCTAssertEqual(Event.PresetEvent.FIRST_OPEN, events[0].eventType)
111-
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[1].eventType)
112-
XCTAssertEqual(Event.PresetEvent.USER_ENGAGEMENT, events[2].eventType)
113+
XCTAssertEqual(Event.PresetEvent.APP_START, events[1].eventType)
114+
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[2].eventType)
115+
XCTAssertEqual(Event.PresetEvent.USER_ENGAGEMENT, events[3].eventType)
113116
}
114117

115118
func testReturnToForeground() {
@@ -137,9 +140,35 @@ class SessionClientTests: XCTestCase {
137140

138141
Thread.sleep(forTimeInterval: 0.1)
139142
let events = eventRecorder.savedEvents
140-
XCTAssertEqual(3, events.count)
143+
XCTAssertEqual(5, events.count)
141144
XCTAssertEqual(Event.PresetEvent.FIRST_OPEN, events[0].eventType)
142-
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[1].eventType)
145+
XCTAssertEqual(Event.PresetEvent.APP_START, events[1].eventType)
146+
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[2].eventType)
147+
XCTAssertEqual(Event.PresetEvent.APP_START, events[3].eventType)
148+
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[4].eventType)
149+
}
150+
151+
func testReturnToForegroundWithScreenView() {
152+
activityTracker.callback?(.runningInForeground)
153+
let viewController = MockViewControllerA()
154+
let window = UIWindow(frame: UIScreen.main.bounds)
155+
window.rootViewController = viewController
156+
window.makeKeyAndVisible()
157+
activityTracker.callback?(.runningInBackground)
158+
activityTracker.callback?(.runningInForeground)
159+
Thread.sleep(forTimeInterval: 0.1)
160+
let events = eventRecorder.savedEvents
161+
XCTAssertEqual(5, events.count)
162+
XCTAssertEqual(Event.PresetEvent.FIRST_OPEN, events[0].eventType)
163+
XCTAssertEqual(Event.PresetEvent.APP_START, events[1].eventType)
164+
XCTAssertTrue(events[1].attributes[Event.ReservedAttribute.IS_FIRST_TIME] as! Bool)
165+
143166
XCTAssertEqual(Event.PresetEvent.SESSION_START, events[2].eventType)
167+
XCTAssertEqual(Event.PresetEvent.SCREEN_VIEW, events[3].eventType)
168+
XCTAssertEqual(Event.PresetEvent.APP_START, events[4].eventType)
169+
let appStartEvent = events[4]
170+
XCTAssertNotNil(appStartEvent.attributes[Event.ReservedAttribute.SCREEN_NAME])
171+
XCTAssertNotNil(appStartEvent.attributes[Event.ReservedAttribute.SCREEN_ID])
172+
XCTAssertFalse(appStartEvent.attributes[Event.ReservedAttribute.IS_FIRST_TIME] as! Bool)
144173
}
145174
}

Tests/ClickstreamTests/IntegrationTest.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,24 +66,23 @@ class IntegrationTest: XCTestCase {
6666
"Successful": true,
6767
"Score": 90
6868
])
69-
ClickstreamAnalytics.flushEvents()
7069
Thread.sleep(forTimeInterval: 0.2)
7170
let eventCount = try eventRecorder.dbUtil.getEventCount()
72-
XCTAssertEqual(0, eventCount)
71+
XCTAssertEqual(1, eventCount)
7372
}
7473

7574
func testRecordOneEventWithNameSuccess() throws {
7675
ClickstreamAnalytics.recordEvent("testEvent")
77-
ClickstreamAnalytics.flushEvents()
7876
Thread.sleep(forTimeInterval: 0.2)
7977
let eventCount = try eventRecorder.dbUtil.getEventCount()
80-
XCTAssertEqual(0, eventCount)
78+
XCTAssertEqual(1, eventCount)
8179
}
8280

8381
func testFlushEvents() throws {
8482
ClickstreamAnalytics.recordEvent("testEvent")
83+
Thread.sleep(forTimeInterval: 0.1)
8584
ClickstreamAnalytics.flushEvents()
86-
Thread.sleep(forTimeInterval: 0.2)
85+
Thread.sleep(forTimeInterval: 0.5)
8786
let eventCount = try eventRecorder.dbUtil.getEventCount()
8887
XCTAssertEqual(0, eventCount)
8988
}
@@ -95,6 +94,7 @@ class IntegrationTest: XCTestCase {
9594
"class": 5,
9695
"isOpenNotification": true
9796
])
97+
Thread.sleep(forTimeInterval: 0.1)
9898
ClickstreamAnalytics.recordEvent("testEvent")
9999
Thread.sleep(forTimeInterval: 0.1)
100100

@@ -113,7 +113,9 @@ class IntegrationTest: XCTestCase {
113113
"class": 5,
114114
"isOpenNotification": true
115115
])
116+
Thread.sleep(forTimeInterval: 0.1)
116117
ClickstreamAnalytics.deleteGlobalAttributes("channel")
118+
Thread.sleep(forTimeInterval: 0.1)
117119
ClickstreamAnalytics.recordEvent("testEvent")
118120
Thread.sleep(forTimeInterval: 0.1)
119121

@@ -127,13 +129,14 @@ class IntegrationTest: XCTestCase {
127129

128130
func testAddUserAttribute() throws {
129131
ClickstreamAnalytics.setUserId("13232")
132+
Thread.sleep(forTimeInterval: 0.1)
130133
ClickstreamAnalytics.addUserAttributes([
131134
"_user_age": 21,
132135
"isFirstOpen": true,
133136
"score": 85.2,
134137
"_user_name": "carl"
135138
])
136-
Thread.sleep(forTimeInterval: 0.1)
139+
Thread.sleep(forTimeInterval: 0.2)
137140
ClickstreamAnalytics.recordEvent("testEvent")
138141
Thread.sleep(forTimeInterval: 0.1)
139142
let testEvent = try getTestEvent()
@@ -156,6 +159,7 @@ class IntegrationTest: XCTestCase {
156159

157160
func testSetUserIdNil() throws {
158161
ClickstreamAnalytics.setUserId("12345")
162+
Thread.sleep(forTimeInterval: 0.1)
159163
ClickstreamAnalytics.setUserId(nil)
160164
ClickstreamAnalytics.recordEvent("testEvent")
161165
Thread.sleep(forTimeInterval: 0.1)
@@ -220,10 +224,9 @@ class IntegrationTest: XCTestCase {
220224
let configuration = try ClickstreamAnalytics.getClickstreamConfiguration()
221225
configuration.endpoint = testFailEndpoint
222226
ClickstreamAnalytics.recordEvent("testEvent")
223-
ClickstreamAnalytics.flushEvents()
224227
Thread.sleep(forTimeInterval: 0.2)
225228
let eventCount = try eventRecorder.dbUtil.getEventCount()
226-
XCTAssertNotEqual(0, eventCount)
229+
XCTAssertEqual(1, eventCount)
227230
}
228231

229232
func testModifyConfiguration() throws {
@@ -232,10 +235,9 @@ class IntegrationTest: XCTestCase {
232235
configuration.isLogEvents = true
233236
configuration.authCookie = "authCookie"
234237
ClickstreamAnalytics.recordEvent("testEvent")
235-
ClickstreamAnalytics.flushEvents()
236238
Thread.sleep(forTimeInterval: 0.2)
237239
let eventCount = try eventRecorder.dbUtil.getEventCount()
238-
XCTAssertEqual(0, eventCount)
240+
XCTAssertEqual(1, eventCount)
239241
}
240242

241243
// MARK: - Objc test
@@ -249,10 +251,9 @@ class IntegrationTest: XCTestCase {
249251
]
250252
ClickstreamObjc.recordEvent("userId")
251253
ClickstreamObjc.recordEvent("testEvent", attribute)
252-
ClickstreamObjc.flushEvents()
253254
Thread.sleep(forTimeInterval: 0.2)
254255
let eventCount = try eventRecorder.dbUtil.getEventCount()
255-
XCTAssertEqual(0, eventCount)
256+
XCTAssertEqual(2, eventCount)
256257
}
257258

258259
func testGlobalAttributeForObjc() throws {
@@ -263,6 +264,7 @@ class IntegrationTest: XCTestCase {
263264
"level": 5
264265
]
265266
ClickstreamObjc.addGlobalAttributes(attribute)
267+
Thread.sleep(forTimeInterval: 0.1)
266268
ClickstreamObjc.deleteGlobalAttributes(["Channel"])
267269
Thread.sleep(forTimeInterval: 0.1)
268270
ClickstreamObjc.recordEvent("testEvent")
@@ -301,10 +303,9 @@ class IntegrationTest: XCTestCase {
301303
configuration.isCompressEvents = true
302304
configuration.authCookie = "authCookie"
303305
ClickstreamAnalytics.recordEvent("testEvent")
304-
ClickstreamAnalytics.flushEvents()
305306
Thread.sleep(forTimeInterval: 0.2)
306307
let eventCount = try eventRecorder.dbUtil.getEventCount()
307-
XCTAssertEqual(0, eventCount)
308+
XCTAssertEqual(1, eventCount)
308309
}
309310

310311
private func getTestEvent() throws -> [String: Any] {

0 commit comments

Comments
 (0)