Skip to content

Commit

Permalink
feat: support sending events when app move to background (#21)
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 Jun 25, 2023
1 parent 3dfe309 commit ba2e4b5
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 20 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@ do {
configuration.sessionTimeoutDuration = 1800000
configuration.isTrackScreenViewEvents = true
configuration.isLogEvents = true
configuration.isCompressEvents = true
configuration.isLogEvents = true
configuration.isCompressEvents = true
} catch {
print("Failed to config ClickstreamAnalytics: \(error)")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ extension AWSClickstreamPlugin {
log.error("Device is offline, skipping submitting events to Clickstream server")
return
}
analyticsClient.submitEvents()
analyticsClient.submitEvents(isBackgroundMode: false)
}

func getEscapeHatch() -> ClickstreamContext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protocol AnalyticsClientBehaviour {

func createEvent(withEventType eventType: String) -> ClickstreamEvent
func record(_ event: ClickstreamEvent) async throws
func submitEvents()
func submitEvents(isBackgroundMode: Bool)
}

typealias SessionProvider = () -> Session?
Expand Down Expand Up @@ -124,7 +124,7 @@ class AnalyticsClient: AnalyticsClientBehaviour {
try eventRecorder.save(event)
}

func submitEvents() {
eventRecorder.submitEvents()
func submitEvents(isBackgroundMode: Bool = false) {
eventRecorder.submitEvents(isBackgroundMode: isBackgroundMode)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

import Amplify
import Foundation
#if canImport(UIKit)
import UIKit
#endif

/// AnalyticsEventRecording saves and submits clickstream events
protocol AnalyticsEventRecording {
Expand All @@ -15,8 +18,8 @@ protocol AnalyticsEventRecording {
func save(_ event: ClickstreamEvent) throws

/// Submit locally stored events
/// - Returns: A collection of events submitted to Clickstream
func submitEvents()
/// - Parameter isBackgroundMode: whether use background mode to send request
func submitEvents(isBackgroundMode: Bool)
}

/// An AnalyticsEventRecording implementation that stores and submits clickstream events
Expand Down Expand Up @@ -46,8 +49,7 @@ class EventRecorder: AnalyticsEventRecording {
try dbUtil.saveEvent(storageEvent)
if clickstream.configuration.isLogEvents {
setLogLevel(logLevel: LogLevel.debug)
log.debug("saved event: \(event.eventType)")
log.debug(eventJson)
logEventPrettier(event: event)
}
while try dbUtil.getTotalSize() > Constants.maxDbSize {
let events = try dbUtil.getEventsWith(limit: 5)
Expand All @@ -61,10 +63,20 @@ class EventRecorder: AnalyticsEventRecording {
}

/// submit an batch events, add the processEvent() as operation into queue
func submitEvents() {
func submitEvents(isBackgroundMode: Bool = false) {
if queue.operationCount < Constants.maxEventOperations {
let operation = BlockOperation { [weak self] in
_ = self?.processEvent()
if isBackgroundMode {
#if canImport(UIKit)
let taskId = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
self?.log.debug("Start background task")
_ = self?.processEvent()
UIApplication.shared.endBackgroundTask(taskId)
self?.log.debug("Background task is the end")
#endif
} else {
_ = self?.processEvent()
}
}
queue.addOperation(operation)
} else {
Expand Down Expand Up @@ -106,7 +118,6 @@ class EventRecorder: AnalyticsEventRecording {
} catch {
log.error("Failed to send event:\(error)")
}
log.info("Send \(totalEventSend) events in one submit")
return totalEventSend
}

Expand All @@ -133,6 +144,15 @@ class EventRecorder: AnalyticsEventRecording {
}
return BatchEvent(eventsJson: eventsJson, eventCount: eventCount, lastEventId: lastEventId)
}

func logEventPrettier(event: ClickstreamEvent) {
var attributesStr = "attributes:{\n"
for (key, value) in event.attributes {
attributesStr += " \"\(key)\": \(value)\n"
}
attributesStr += "}"
log.debug("Event saved, event name: \(event.eventType)\n\(attributesStr)")
}
}

extension EventRecorder: ClickstreamLogger {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class AutoRecordEventClient {
}
recordEvent(event)
}
clickstream.analyticsClient.submitEvents(isBackgroundMode: true)
}

func updateEngageTimestamp() {
Expand Down
30 changes: 26 additions & 4 deletions Tests/ClickstreamTests/Clickstream/EventRecorderTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class EventRecorderTest: XCTestCase {
var eventRecorder: EventRecorder!
var clickstream: ClickstreamContext!
var server: HttpServer!
var activityTracker: MockActivityTracker!

override func setUp() async throws {
do {
Expand Down Expand Up @@ -52,6 +53,23 @@ class EventRecorderTest: XCTestCase {
netWorkType: NetWorkType.Wifi)
eventRecorder = try! EventRecorder(clickstream: clickstream)
dbUtil = eventRecorder.dbUtil

activityTracker = MockActivityTracker()
let 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
)
clickstream.analyticsClient = analyticsClient
clickstream.networkMonitor = MockNetworkMonitor()
} catch {
XCTFail("Fail to setup EventRecorder error:\(error)")
}
Expand Down Expand Up @@ -342,7 +360,7 @@ class EventRecorderTest: XCTestCase {

eventRecorder.submitEvents()
XCTAssertEqual(1, eventRecorder.queue.operationCount)
Thread.sleep(forTimeInterval: 0.3)
Thread.sleep(forTimeInterval: 0.5)
let totalEvent = try dbUtil.getEventCount()
XCTAssertEqual(0, totalEvent)
XCTAssertTrue(eventRecorder.bundleSequenceId == 3)
Expand Down Expand Up @@ -376,9 +394,6 @@ class EventRecorderTest: XCTestCase {
eventRecorder.submitEvents()
eventRecorder.submitEvents()
XCTAssertEqual(2, eventRecorder.queue.operationCount)
Thread.sleep(forTimeInterval: 0.5)
let totalEvent = try dbUtil.getEventCount()
XCTAssertEqual(0, totalEvent)
}

func testProcessEventQueueReachedMaxOperationCount() throws {
Expand All @@ -390,6 +405,13 @@ class EventRecorderTest: XCTestCase {
}
XCTAssertTrue(eventRecorder.queue.operationCount <= 1_000)
}

func testBackgroundModeAutoSendRequest() throws {
activityTracker.callback?(.runningInForeground)
try eventRecorder.save(clickstreamEvent)
activityTracker.callback?(.runningInBackground)
XCTAssertTrue(eventRecorder.queue.operationCount > 0)
}
}

extension String {
Expand Down
4 changes: 3 additions & 1 deletion Tests/ClickstreamTests/IntegrationTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,9 @@ class IntegrationTest: XCTestCase {
configuration.isCompressEvents = true
configuration.isLogEvents = true
configuration.authCookie = "authCookie"
ClickstreamAnalytics.recordEvent("testEvent")
ClickstreamAnalytics.recordEvent("testEvent", [
"isLogEvent": true
])
Thread.sleep(forTimeInterval: 0.2)
let eventCount = try eventRecorder.dbUtil.getEventCount()
XCTAssertEqual(1, eventCount)
Expand Down
2 changes: 1 addition & 1 deletion Tests/ClickstreamTests/Mock/MockAnalyticsClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class MockAnalyticsClient: AnalyticsClientBehaviour {
}

var submitEventsCount = 0
func submitEvents() {
func submitEvents(isBackgroundMode: Bool = false) {
submitEventsCount += 1
submitEventsExpectation?.fulfill()
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/ClickstreamTests/Mock/MockEventRecorder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class MockEventRecorder: AnalyticsEventRecording {
}

var submitCount = 0
func submitEvents() {
func submitEvents(isBackgroundMode: Bool = false) {
submitCount += 1
}
}

0 comments on commit ba2e4b5

Please sign in to comment.