Skip to content

Commit

Permalink
fix: sessionId in first open and session start events (#54)
Browse files Browse the repository at this point in the history
Co-authored-by: xiaoweii <xiaoweii@amazom.com>
  • Loading branch information
zhu-xiaowei and xiaoweii authored Mar 5, 2024
1 parent 63e1df1 commit ee3abff
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 112 deletions.
13 changes: 4 additions & 9 deletions Sources/Clickstream/AWSClickstreamPlugin+Configure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,15 @@ extension AWSClickstreamPlugin {

let sessionClient = SessionClient(clickstream: clickstream)
clickstream.sessionClient = sessionClient
let sessionProvider: () -> Session? = { [weak sessionClient] in
guard let sessionClient else {
fatalError("SessionClient was deallocated")
}
return sessionClient.getCurrentSession()
}
let eventRecorder = try EventRecorder(clickstream: clickstream)
analyticsClient = try AnalyticsClient(clickstream: clickstream,
eventRecorder: eventRecorder,
sessionProvider: sessionProvider)
sessionClient: sessionClient)
clickstream.analyticsClient = analyticsClient

let networkMonitor = NWPathMonitor()
clickstream.networkMonitor = networkMonitor
sessionClient.initialSession()
sessionClient.startActivityTracking()
var autoFlushEventsTimer: DispatchSourceTimer?
if configuration.sendEventsInterval != 0 {
let timeInterval = TimeInterval(Double(configuration.sendEventsInterval) / 1_000)
Expand All @@ -66,7 +61,7 @@ extension AWSClickstreamPlugin {
autoFlushEventsTimer: autoFlushEventsTimer,
networkMonitor: networkMonitor
)
log.debug("init the sdk success")
log.debug("initialize Clickstream SDK successful")
}

// MARK: Internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,27 @@ protocol AnalyticsClientBehaviour {
func submitEvents(isBackgroundMode: Bool)
}

typealias SessionProvider = () -> Session?

class AnalyticsClient: AnalyticsClientBehaviour {
private(set) var eventRecorder: AnalyticsEventRecording
private let sessionProvider: SessionProvider
private let sessionClient: SessionClient
private(set) lazy var globalAttributes: [String: AttributeValue] = [:]
private(set) var allUserAttributes: [String: Any] = [:]
private(set) var simpleUserAttributes: [String: Any] = [:]
private let clickstream: ClickstreamContext
private(set) var userId: String?
var autoRecordClient: AutoRecordEventClient?
var autoRecordClient: AutoRecordEventClient

init(clickstream: ClickstreamContext,
eventRecorder: AnalyticsEventRecording,
sessionProvider: @escaping SessionProvider) throws
sessionClient: SessionClient) throws
{
self.clickstream = clickstream
self.eventRecorder = eventRecorder
self.sessionProvider = sessionProvider
self.sessionClient = sessionClient
self.userId = UserDefaultsUtil.getCurrentUserId(storage: clickstream.storage)
self.allUserAttributes = UserDefaultsUtil.getUserAttributes(storage: clickstream.storage)
self.autoRecordClient = sessionClient.autoRecordClient
self.simpleUserAttributes = getSimpleUserAttributes()
self.autoRecordClient = (clickstream.sessionClient as? SessionClient)?.autoRecordClient
}

func addGlobalAttribute(_ attribute: AttributeValue, forKey key: String) {
Expand Down Expand Up @@ -116,7 +114,7 @@ class AnalyticsClient: AnalyticsClientBehaviour {
let event = ClickstreamEvent(eventType: eventType,
appId: clickstream.configuration.appId,
uniqueId: clickstream.userUniqueId,
session: sessionProvider(),
session: sessionClient.getCurrentSession(),
systemInfo: clickstream.systemInfo,
netWorkType: clickstream.networkMonitor.netWorkType)
return event
Expand All @@ -135,15 +133,13 @@ class AnalyticsClient: AnalyticsClientBehaviour {
for (key, attribute) in globalAttributes {
event.addGlobalAttribute(attribute, forKey: key)
}
if let autoRecordClient {
if autoRecordClient.lastScreenName != nil {
event.addGlobalAttribute(autoRecordClient.lastScreenName!,
forKey: Event.ReservedAttribute.SCREEN_NAME)
}
if autoRecordClient.lastScreenUniqueId != nil {
event.addGlobalAttribute(autoRecordClient.lastScreenUniqueId!,
forKey: Event.ReservedAttribute.SCREEN_UNIQUEID)
}
if autoRecordClient.lastScreenName != nil {
event.addGlobalAttribute(autoRecordClient.lastScreenName!,
forKey: Event.ReservedAttribute.SCREEN_NAME)
}
if autoRecordClient.lastScreenUniqueId != nil {
event.addGlobalAttribute(autoRecordClient.lastScreenUniqueId!,
forKey: Event.ReservedAttribute.SCREEN_UNIQUEID)
}
if event.eventType == Event.PresetEvent.PROFILE_SET {
event.setUserAttribute(allUserAttributes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ class AutoRecordEventClient {
if !clickstream.configuration.isTrackScreenViewEvents {
return
}
let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW)
recordScreenViewEvent(event, screenName, screenPath, screenUniqueId)
if clickstream.analyticsClient != nil {
let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW)
recordScreenViewEvent(event, screenName, screenPath, screenUniqueId)
}
}

func recordViewScreenManually(_ event: ClickstreamEvent) {
Expand Down Expand Up @@ -176,17 +178,18 @@ class AutoRecordEventClient {
}
}

func handleAppStart() {
if isFirstTime {
checkAppVersionUpdate(clickstream: clickstream)
checkOSVersionUpdate(clickstream: clickstream)
}
func handleFirstOpen() {
checkAppVersionUpdate(clickstream: clickstream)
checkOSVersionUpdate(clickstream: clickstream)
if isFirstOpen {
let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.FIRST_OPEN)
recordEvent(event)
UserDefaultsUtil.saveIsFirstOpen(storage: clickstream.storage, isFirstOpen: "false")
isFirstOpen = false
}
}

func handleAppStart() {
let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.APP_START)
event.addAttribute(isFirstTime, forKey: Event.ReservedAttribute.IS_FIRST_TIME)
recordEvent(event)
Expand Down Expand Up @@ -229,7 +232,7 @@ class AutoRecordEventClient {

func recordEvent(_ event: ClickstreamEvent) {
do {
try clickstream.analyticsClient.record(event)
try clickstream.analyticsClient?.record(event)
} catch {
log.error("Failed to record event with error:\(error)")
}
Expand Down
20 changes: 12 additions & 8 deletions Sources/Clickstream/Dependency/Clickstream/Session/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Session: Codable {
let startTime: Int64
let sessionIndex: Int
private(set) var pauseTime: Int64?
var isRecorded = false

init(uniqueId: String, sessionIndex: Int) {
self.sessionId = Self.generateSessionId(uniqueId: uniqueId)
Expand All @@ -27,19 +28,22 @@ class Session: Codable {
self.sessionIndex = sessionIndex
}

static func getCurrentSession(clickstream: ClickstreamContext) -> Session {
let storedSession = UserDefaultsUtil.getSession(storage: clickstream.storage)
var sessionIndex = 1
if storedSession != nil {
if Date().millisecondsSince1970 - storedSession!.pauseTime!
static func getCurrentSession(clickstream: ClickstreamContext, previousSession: Session? = nil) -> Session {
var session = previousSession
if session == nil {
session = UserDefaultsUtil.getSession(storage: clickstream.storage)
}
if session != nil {
if session!.isNewSession || Date().millisecondsSince1970 - session!.pauseTime!
< clickstream.configuration.sessionTimeoutDuration
{
return storedSession!
return session!
} else {
sessionIndex = storedSession!.sessionIndex + 1
return Session(uniqueId: clickstream.userUniqueId, sessionIndex: session!.sessionIndex + 1)
}
} else {
return Session(uniqueId: clickstream.userUniqueId, sessionIndex: 1)
}
return Session(uniqueId: clickstream.userUniqueId, sessionIndex: sessionIndex)
}

var isNewSession: Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import Foundation

protocol SessionClientBehaviour: AnyObject {
func startActivityTracking()
func initialSession()
func storeSession()
func onManualScreenView(_ event: ClickstreamEvent)
}

class SessionClient: SessionClientBehaviour {
private var session: Session?
private var session: Session
private let clickstream: ClickstreamContext
private let activityTracker: ActivityTrackerBehaviour
private let sessionClientQueue = DispatchQueue(label: Constants.queue,
Expand All @@ -25,9 +24,9 @@ class SessionClient: SessionClientBehaviour {

init(activityTracker: ActivityTrackerBehaviour? = nil, clickstream: ClickstreamContext) {
self.clickstream = clickstream
self.activityTracker = activityTracker ?? ActivityTracker()
self.session = Session.getCurrentSession(clickstream: clickstream)
self.autoRecordClient = AutoRecordEventClient(clickstream: clickstream)
startActivityTracking()
self.activityTracker = activityTracker ?? ActivityTracker()
}

func startActivityTracking() {
Expand All @@ -39,31 +38,31 @@ class SessionClient: SessionClientBehaviour {
}
}

func initialSession() {
session = Session.getCurrentSession(clickstream: clickstream)
if session!.isNewSession {
autoRecordClient.recordSessionStartEvent()
autoRecordClient.setIsEntrances()
autoRecordClient.recordScreenViewAfterSessionStart()
}
}

func storeSession() {
if session != nil {
session?.pause()
UserDefaultsUtil.saveSession(storage: clickstream.storage, session: session!)
}
session.pause()
UserDefaultsUtil.saveSession(storage: clickstream.storage, session: session)
}

func getCurrentSession() -> Session? {
func getCurrentSession() -> Session {
session
}

private func handleAppEnterForeground() {
log.debug("Application entered the foreground.")
autoRecordClient.handleFirstOpen()
session = Session.getCurrentSession(clickstream: clickstream, previousSession: session)
autoRecordClient.handleAppStart()
autoRecordClient.updateLastScreenStartTimestamp(Date().millisecondsSince1970)
initialSession()
handleSesionStart()
}

private func handleSesionStart() {
if session.isNewSession, !session.isRecorded {
autoRecordClient.recordSessionStartEvent()
autoRecordClient.setIsEntrances()
autoRecordClient.recordScreenViewAfterSessionStart()
session.isRecorded = true
}
}

private func handleAppEnterBackground() {
Expand Down
9 changes: 5 additions & 4 deletions Tests/ClickstreamTests/Clickstream/AnalyticsClientTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ class AnalyticsClientTest: XCTestCase {
isTrackAppExceptionEvents: false,
isCompressEvents: false)
clickstream = try ClickstreamContext(with: contextConfiguration)
let sessionClient = SessionClient(clickstream: clickstream)
clickstream.sessionClient = sessionClient

clickstream.networkMonitor = MockNetworkMonitor()
eventRecorder = MockEventRecorder()
session = Session(uniqueId: "uniqueId", sessionIndex: 1)
session = sessionClient.getCurrentSession()
analyticsClient = try AnalyticsClient(
clickstream: clickstream,
eventRecorder: eventRecorder,
sessionProvider: {
self.session
}
sessionClient: sessionClient
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class AutoRecordEventClientTest: XCTestCase {
let analyticsClient = try AnalyticsClient(
clickstream: clickstream,
eventRecorder: eventRecorder,
sessionProvider: { nil }
sessionClient: sessionClient
)
clickstream.analyticsClient = analyticsClient

Expand Down Expand Up @@ -78,6 +78,7 @@ class AutoRecordEventClientTest: XCTestCase {
}

func testOneScreenView() {
sessionClient.startActivityTracking()
activityTracker.callback?(.runningInForeground)
autoRecordEventClient.setIsEntrances()
let viewController = MockViewControllerA()
Expand Down
12 changes: 4 additions & 8 deletions Tests/ClickstreamTests/Clickstream/EventRecorderTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class EventRecorderTest: XCTestCase {
var clickstream: ClickstreamContext!
var server: HttpServer!
var activityTracker: MockActivityTracker!
var sessionClient: SessionClient!

override func setUp() async throws {
do {
Expand Down Expand Up @@ -56,18 +57,12 @@ class EventRecorderTest: XCTestCase {
dbUtil = eventRecorder.dbUtil

activityTracker = MockActivityTracker()
let sessionClient = SessionClient(activityTracker: activityTracker, clickstream: clickstream)
sessionClient = SessionClient(activityTracker: activityTracker, clickstream: clickstream)
clickstream.sessionClient = sessionClient
let sessionProvider: () -> Session? = { [weak sessionClient] in
guard let sessionClient else {
fatalError("SessionClient was deallocated")
}
return sessionClient.getCurrentSession()
}
let analyticsClient = try AnalyticsClient(
clickstream: clickstream,
eventRecorder: eventRecorder,
sessionProvider: sessionProvider
sessionClient: sessionClient
)
clickstream.analyticsClient = analyticsClient
clickstream.networkMonitor = MockNetworkMonitor()
Expand Down Expand Up @@ -408,6 +403,7 @@ class EventRecorderTest: XCTestCase {
}

func testBackgroundModeAutoSendRequest() throws {
sessionClient.startActivityTracking()
activityTracker.callback?(.runningInForeground)
try eventRecorder.save(clickstreamEvent)
activityTracker.callback?(.runningInBackground)
Expand Down
Loading

0 comments on commit ee3abff

Please sign in to comment.