Skip to content

Commit

Permalink
feat: support record screen view event manually (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhu-xiaowei authored Feb 22, 2024
1 parent 16328f2 commit 5e1b524
Show file tree
Hide file tree
Showing 12 changed files with 264 additions and 30 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,24 @@ let item_book: ClickstreamAttribute = [
ClickstreamAnalytics.recordEvent("view_item", attributes, [item_book])
```

#### Record Screen View events manually

By default, SDK will automatically track the preset `_screen_view` event when ViewController triggers `viewDidAppear`.

You can also manually record screen view events whether or not automatic screen view tracking is enabled, add the following code to record a screen view event with two attributes.

* `SCREEN_NAME` Required. Your screen's name.
* `SCREEN_UNIQUE_ID` Optional. Set the hashValue of your ViewController or UIView. If you do not set, SDK will set a default value based on the current ViewController's hashValue.

```swift
import Clickstream

ClickstreamAnalytics.recordEvent(ClickstreamAnalytics.EventName.SCREEN_VIEW, [
ClickstreamAnalytics.Attr.SCREEN_NAME: "HomeView",
ClickstreamAnalytics.Attr.SCREEN_UNIQUE_ID: homeView.hashValue
])
```

#### Add global attribute

```swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,17 @@ extension AWSClickstreamPlugin {
if let attributes = event.attribute {
clickstreamEvent.addAttribute(attributes)
}
if event.name == Event.PresetEvent.SCREEN_VIEW {
clickstream.sessionClient.onManualScreenView(clickstreamEvent)
return
}
if let items = event.items {
clickstreamEvent.addItems(items)
}

Task {
do {
try await analyticsClient.record(clickstreamEvent)
try analyticsClient.record(clickstreamEvent)
} catch {
log.error("Record event error:\(error)")
}
Expand Down
11 changes: 11 additions & 0 deletions Sources/Clickstream/ClickstreamAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ public enum ClickstreamAnalytics {
Amplify.Analytics.enable()
}

/// ClickstreamAnalytics preset events
public enum EventName {
public static let SCREEN_VIEW = "_screen_view"
}

/// ClickstreamANalytics preset attributes
public enum Attr {
public static let SCREEN_NAME = "_screen_name"
public static let SCREEN_UNIQUE_ID = "_screen_unique_id"
}

/// ClickstreamAnalytics preset item attributes
/// In addition to the item attributes defined below, you can add up to 10 custom attributes to an item.
public enum Item {
Expand Down
14 changes: 14 additions & 0 deletions Sources/Clickstream/ClickstreamObjc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ import Foundation
}
}

/// ClickstreamAnalytics preset events
@objcMembers public class EventName: NSObject {
/// Preset event screen view
public static let SCREEN_VIEW = "_screen_view"
}

/// ClickstreamANalytics preset attributes
@objcMembers public class Attr: NSObject {
/// Preset attribute screen name
public static let SCREEN_NAME = "_screen_name"
/// Preset attribute screen unique id
public static let SCREEN_UNIQUE_ID = "_screen_unique_id"
}

/// ClickstreamAnalytics preset item keys for objective-c
/// In addition to the item attributes defined below, you can add up to 10 custom attributes to an item.
@objcMembers public class ClickstreamItemKey: NSObject {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protocol AnalyticsClientBehaviour {

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

Expand Down Expand Up @@ -131,14 +131,16 @@ class AnalyticsClient: AnalyticsClientBehaviour {
return true
}

func record(_ event: ClickstreamEvent) async throws {
func record(_ event: ClickstreamEvent) throws {
for (key, attribute) in globalAttributes {
event.addGlobalAttribute(attribute, forKey: key)
}
if let autoRecordClient {
if autoRecordClient.lastScreenName != nil, autoRecordClient.lastScreenUniqueId != nil {
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)
}
Expand All @@ -157,7 +159,7 @@ class AnalyticsClient: AnalyticsClientBehaviour {
let event = createEvent(withEventType: Event.PresetEvent.CLICKSTREAM_ERROR)
event.addAttribute(eventError.errorCode, forKey: Event.ReservedAttribute.ERROR_CODE)
event.addAttribute(eventError.errorMessage, forKey: Event.ReservedAttribute.ERROR_MESSAGE)
try await record(event)
try record(event)
} catch {
log.error("Failed to record event with error:\(error)")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,60 @@ class AutoRecordEventClient {

func onViewDidAppear(screenName: String, screenPath: String, screenHashValue: String) {
if !clickstream.isEnable { return }
if !isSameScreen(screenName, screenPath, screenHashValue) {
if !isSameScreen(screenName, screenHashValue) {
if lastScreenName != nil {
recordUserEngagement()
}
recordScreenView(screenName, screenPath, screenHashValue)
recordViewScreenAutomatically(screenName, screenPath, screenHashValue)
}
}

func recordScreenView(_ screenName: String, _ screenPath: String, _ screenUniqueId: String) {
func recordViewScreenAutomatically(_ screenName: String, _ screenPath: String, _ screenUniqueId: String) {
if !clickstream.configuration.isTrackScreenViewEvents {
return
}
let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW)
recordScreenViewEvent(event, screenName, screenPath, screenUniqueId)
}

func recordViewScreenManually(_ event: ClickstreamEvent) {
if let screenName = event.attribute(forKey: Event.ReservedAttribute.SCREEN_NAME) as? String {
var screenUniqueId = event.attribute(forKey: Event.ReservedAttribute.SCREEN_UNIQUEID) as? String
if screenUniqueId == nil {
screenUniqueId = lastScreenUniqueId
}
if isSameScreen(screenName, screenUniqueId) {
return
}
if lastScreenName != nil {
recordUserEngagement()
}
recordScreenViewEvent(event, screenName, lastScreenPath, screenUniqueId)
} else {
let errorEvent = clickstream.analyticsClient.createEvent(withEventType:
Event.PresetEvent.CLICKSTREAM_ERROR)
errorEvent.addAttribute(Event.ErrorCode.SCREEN_VIEW_MISSING_SCREEN_NAME,
forKey: Event.ReservedAttribute.ERROR_CODE)
errorEvent.addAttribute("record an screen view event without the required screen name attribute",
forKey: Event.ReservedAttribute.ERROR_MESSAGE)
recordEvent(errorEvent)
}
}

func recordScreenViewEvent(_ event: ClickstreamEvent, _ screenName: String,
_ screenPath: String?, _ screenUniqueId: String?)
{
let eventTimestamp = event.timestamp
event.addAttribute(screenPath, forKey: Event.ReservedAttribute.SCREEN_ID)
if lastScreenName != nil, lastScreenPath != nil, lastScreenUniqueId != nil {
if screenPath != nil {
event.addAttribute(screenPath!, forKey: Event.ReservedAttribute.SCREEN_ID)
}
if lastScreenName != nil {
event.addAttribute(lastScreenName!, forKey: Event.ReservedAttribute.PREVIOUS_SCREEN_NAME)
}
if lastScreenPath != nil {
event.addAttribute(lastScreenPath!, forKey: Event.ReservedAttribute.PREVIOUS_SCREEN_ID)
}
if lastScreenUniqueId != nil {
event.addAttribute(lastScreenUniqueId!, forKey: Event.ReservedAttribute.PREVIOUS_SCREEN_UNIQUEID)
}
let previousTimestamp = getPreviousScreenViewTimestamp()
Expand All @@ -64,12 +100,11 @@ class AutoRecordEventClient {
if lastEngageTime > 0 {
event.addAttribute(lastEngageTime, forKey: Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP)
}
recordEvent(event)

isEntrances = false
lastScreenName = screenName
lastScreenPath = screenPath
lastScreenUniqueId = screenUniqueId
recordEvent(event)
isEntrances = false
lastScreenStartTimestamp = eventTimestamp
UserDefaultsUtil.savePreviousScreenViewTimestamp(storage: clickstream.storage, timestamp: eventTimestamp)
}
Expand All @@ -92,11 +127,9 @@ class AutoRecordEventClient {
UserDefaultsUtil.getPreviousScreenViewTimestamp(storage: clickstream.storage)
}

func isSameScreen(_ screenName: String, _ screenPath: String, _ screenUniqueId: String) -> Bool {
func isSameScreen(_ screenName: String, _ screenUniqueId: String?) -> Bool {
lastScreenName != nil
&& lastScreenPath != nil
&& screenName == lastScreenName
&& screenPath == lastScreenPath
&& screenUniqueId == lastScreenUniqueId
}

Expand Down Expand Up @@ -182,12 +215,10 @@ class AutoRecordEventClient {
}

func recordEvent(_ event: ClickstreamEvent) {
Task {
do {
try await clickstream.analyticsClient.record(event)
} catch {
log.error("Failed to record event with error:\(error)")
}
do {
try clickstream.analyticsClient.record(event)
} catch {
log.error("Failed to record event with error:\(error)")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,6 @@ enum Event {
static let ITEM_CUSTOM_ATTRIBUTE_SIZE_EXCEED = 4_003
static let ITEM_CUSTOM_ATTRIBUTE_KEY_LENGTH_EXCEED = 4_004
static let ITEM_CUSTOM_ATTRIBUTE_KEY_INVALID = 4_005
static let SCREEN_VIEW_MISSING_SCREEN_NAME = 5_001
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ protocol SessionClientBehaviour: AnyObject {
func startActivityTracking()
func initialSession()
func storeSession()
func onManualScreenView(_ event: ClickstreamEvent)
}

class SessionClient: SessionClientBehaviour {
Expand Down Expand Up @@ -72,6 +73,10 @@ class SessionClient: SessionClientBehaviour {
clickstream.analyticsClient.submitEvents(isBackgroundMode: true)
}

func onManualScreenView(_ event: ClickstreamEvent) {
autoRecordClient.recordViewScreenManually(event)
}

private func respond(to newState: ApplicationState) {
if !clickstream.isEnable { return }
switch newState {
Expand Down
4 changes: 2 additions & 2 deletions Tests/ClickstreamTests/Clickstream/AnalyticsClientTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ class AnalyticsClientTest: XCTestCase {
analyticsClient.addGlobalAttribute(1, forKey: "metric_1")

do {
try await analyticsClient.record(event)
try analyticsClient.record(event)
XCTAssertEqual(eventRecorder.saveCount, 1)
guard let savedEvent = eventRecorder.lastSavedEvent else {
XCTFail("Expected saved event")
Expand All @@ -300,7 +300,7 @@ class AnalyticsClientTest: XCTestCase {
analyticsClient.addUserAttribute(1, forKey: "metric_1")

do {
try await analyticsClient.record(event)
try analyticsClient.record(event)
XCTAssertEqual(eventRecorder.saveCount, 1)
guard let savedEvent = eventRecorder.lastSavedEvent else {
XCTFail("Expected saved event")
Expand Down
Loading

0 comments on commit 5e1b524

Please sign in to comment.