diff --git a/README.md b/README.md index 54178a6..783ee54 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/Sources/Clickstream/AWSClickstreamPlugin+ClientBehavior.swift b/Sources/Clickstream/AWSClickstreamPlugin+ClientBehavior.swift index 2208072..0288957 100644 --- a/Sources/Clickstream/AWSClickstreamPlugin+ClientBehavior.swift +++ b/Sources/Clickstream/AWSClickstreamPlugin+ClientBehavior.swift @@ -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)") } diff --git a/Sources/Clickstream/ClickstreamAnalytics.swift b/Sources/Clickstream/ClickstreamAnalytics.swift index 8527fa2..4a53c40 100644 --- a/Sources/Clickstream/ClickstreamAnalytics.swift +++ b/Sources/Clickstream/ClickstreamAnalytics.swift @@ -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 { diff --git a/Sources/Clickstream/ClickstreamObjc.swift b/Sources/Clickstream/ClickstreamObjc.swift index 26d5f2b..1e1d2bf 100644 --- a/Sources/Clickstream/ClickstreamObjc.swift +++ b/Sources/Clickstream/ClickstreamObjc.swift @@ -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 { diff --git a/Sources/Clickstream/Dependency/Clickstream/Analytics/AnalyticsClient.swift b/Sources/Clickstream/Dependency/Clickstream/Analytics/AnalyticsClient.swift index 853345f..3c511e0 100644 --- a/Sources/Clickstream/Dependency/Clickstream/Analytics/AnalyticsClient.swift +++ b/Sources/Clickstream/Dependency/Clickstream/Analytics/AnalyticsClient.swift @@ -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) } @@ -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) } @@ -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)") } diff --git a/Sources/Clickstream/Dependency/Clickstream/AutoRecord/AutoRecordEventClient.swift b/Sources/Clickstream/Dependency/Clickstream/AutoRecord/AutoRecordEventClient.swift index 5e4acc9..b879aaa 100644 --- a/Sources/Clickstream/Dependency/Clickstream/AutoRecord/AutoRecordEventClient.swift +++ b/Sources/Clickstream/Dependency/Clickstream/AutoRecord/AutoRecordEventClient.swift @@ -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() @@ -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) } @@ -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 } @@ -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)") } } } diff --git a/Sources/Clickstream/Dependency/Clickstream/Event/Event.swift b/Sources/Clickstream/Dependency/Clickstream/Event/Event.swift index 2b0ae84..4fe2e1e 100644 --- a/Sources/Clickstream/Dependency/Clickstream/Event/Event.swift +++ b/Sources/Clickstream/Dependency/Clickstream/Event/Event.swift @@ -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 } } diff --git a/Sources/Clickstream/Dependency/Clickstream/Session/SessionClient.swift b/Sources/Clickstream/Dependency/Clickstream/Session/SessionClient.swift index b3734ed..66923c8 100644 --- a/Sources/Clickstream/Dependency/Clickstream/Session/SessionClient.swift +++ b/Sources/Clickstream/Dependency/Clickstream/Session/SessionClient.swift @@ -12,6 +12,7 @@ protocol SessionClientBehaviour: AnyObject { func startActivityTracking() func initialSession() func storeSession() + func onManualScreenView(_ event: ClickstreamEvent) } class SessionClient: SessionClientBehaviour { @@ -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 { diff --git a/Tests/ClickstreamTests/Clickstream/AnalyticsClientTest.swift b/Tests/ClickstreamTests/Clickstream/AnalyticsClientTest.swift index c76d320..afe31d8 100644 --- a/Tests/ClickstreamTests/Clickstream/AnalyticsClientTest.swift +++ b/Tests/ClickstreamTests/Clickstream/AnalyticsClientTest.swift @@ -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") @@ -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") diff --git a/Tests/ClickstreamTests/Clickstream/AutoRecordEventClientTest.swift b/Tests/ClickstreamTests/Clickstream/AutoRecordEventClientTest.swift index 863e650..a24bf0a 100644 --- a/Tests/ClickstreamTests/Clickstream/AutoRecordEventClientTest.swift +++ b/Tests/ClickstreamTests/Clickstream/AutoRecordEventClientTest.swift @@ -154,10 +154,7 @@ class AutoRecordEventClientTest: XCTestCase { XCTAssertTrue(viewControllerA.viewDidAppearCalled) XCTAssertTrue(viewControllerB.viewDidAppearCalled) let event0 = eventRecorder.savedEvents[0] - var engagementEvent = eventRecorder.savedEvents[1] - if engagementEvent.eventType != Event.PresetEvent.USER_ENGAGEMENT { - engagementEvent = eventRecorder.savedEvents[2] - } + let engagementEvent = eventRecorder.savedEvents[1] XCTAssertEqual(Event.PresetEvent.SCREEN_VIEW, event0.eventType) XCTAssertEqual(event0.attributes[Event.ReservedAttribute.SCREEN_NAME] as! String, engagementEvent.attributes[Event.ReservedAttribute.SCREEN_NAME] as! String) @@ -198,6 +195,97 @@ class AutoRecordEventClientTest: XCTestCase { XCTAssertNotEqual(Event.PresetEvent.USER_ENGAGEMENT, eventRecorder.savedEvents[1].eventType) } + func testRecordScreenViewManuallyWithoutScreenName() { + let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW) + autoRecordEventClient.recordViewScreenManually(event) + let savedEvent = eventRecorder.savedEvents[0] + XCTAssertEqual(Event.PresetEvent.CLICKSTREAM_ERROR, savedEvent.eventType) + XCTAssertEqual(Event.ErrorCode.SCREEN_VIEW_MISSING_SCREEN_NAME, savedEvent.attributes[Event.ReservedAttribute.ERROR_CODE] as! Int) + } + + func testRecordScreenViewManuallyWithInvalidScreenName() { + let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW) + event.addAttribute(123, forKey: ClickstreamAnalytics.Attr.SCREEN_NAME) + autoRecordEventClient.recordViewScreenManually(event) + let savedEvent = eventRecorder.savedEvents[0] + XCTAssertEqual(Event.PresetEvent.CLICKSTREAM_ERROR, savedEvent.eventType) + XCTAssertEqual(Event.ErrorCode.SCREEN_VIEW_MISSING_SCREEN_NAME, savedEvent.attributes[Event.ReservedAttribute.ERROR_CODE] as! Int) + } + + func testRecordScreenViewManuallyWithScreenName() { + let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW) + event.addAttribute("HomeView", forKey: ClickstreamAnalytics.Attr.SCREEN_NAME) + autoRecordEventClient.recordViewScreenManually(event) + let savedEvent = eventRecorder.savedEvents[0] + XCTAssertEqual(Event.PresetEvent.SCREEN_VIEW, savedEvent.eventType) + } + + func testRecordSameScreenViewTwiceManually() { + let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW) + event.addAttribute("HomeView", forKey: ClickstreamAnalytics.Attr.SCREEN_NAME) + autoRecordEventClient.recordViewScreenManually(event) + autoRecordEventClient.recordViewScreenManually(event) + let savedEvent = eventRecorder.savedEvents[0] + XCTAssertEqual(Event.PresetEvent.SCREEN_VIEW, savedEvent.eventType) + XCTAssertEqual(1, eventRecorder.savedEvents.count) + } + + func testRecordTwoDifferentScreenViewManually() { + let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW) + event.addAttribute("HomeView", forKey: ClickstreamAnalytics.Attr.SCREEN_NAME) + autoRecordEventClient.recordViewScreenManually(event) + let event2 = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW) + event2.addAttribute("HomeView2", forKey: ClickstreamAnalytics.Attr.SCREEN_NAME) + autoRecordEventClient.recordViewScreenManually(event2) + XCTAssertEqual(2, eventRecorder.savedEvents.count) + } + + func testRecordCustomScreenViewBetweenTwoScreenView() { + autoRecordEventClient.setIsEntrances() + // auto record screen view for viewControllerA + let viewControllerA = MockViewControllerA() + let viewControllerB = MockViewControllerB() + let window = UIWindow(frame: UIScreen.main.bounds) + window.rootViewController = viewControllerA + window.makeKeyAndVisible() + + // recored custom screen view with user engagement + autoRecordEventClient.updateLastScreenStartTimestamp(Date().millisecondsSince1970 - 1_100) + Thread.sleep(forTimeInterval: 0.02) + let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW) + event.addAttribute("SomeInnerView", forKey: ClickstreamAnalytics.Attr.SCREEN_NAME) + event.addAttribute("abc123ef", forKey: ClickstreamAnalytics.Attr.SCREEN_UNIQUE_ID) + autoRecordEventClient.recordViewScreenManually(event) + + // auto record screen view for viewControllerA with user engagement + autoRecordEventClient.updateLastScreenStartTimestamp(Date().millisecondsSince1970 - 1_100) + Thread.sleep(forTimeInterval: 0.02) + window.rootViewController = viewControllerB + window.makeKeyAndVisible() + + XCTAssertTrue(viewControllerA.viewDidAppearCalled) + XCTAssertTrue(viewControllerB.viewDidAppearCalled) + + let screenViewEventA = eventRecorder.savedEvents[0] + let engagementEvent1 = eventRecorder.savedEvents[1] + let customScreenViewEvent = eventRecorder.savedEvents[2] + let engagementEvent2 = eventRecorder.savedEvents[3] + let screenViewEventB = eventRecorder.savedEvents[4] + + XCTAssertEqual(Event.PresetEvent.SCREEN_VIEW, screenViewEventA.eventType) + XCTAssertEqual(Event.PresetEvent.USER_ENGAGEMENT, engagementEvent1.eventType) + XCTAssertEqual(Event.PresetEvent.SCREEN_VIEW, customScreenViewEvent.eventType) + XCTAssertEqual(Event.PresetEvent.USER_ENGAGEMENT, engagementEvent2.eventType) + XCTAssertEqual(Event.PresetEvent.SCREEN_VIEW, screenViewEventB.eventType) + + XCTAssertEqual("SomeInnerView", screenViewEventB.attributes[Event.ReservedAttribute.PREVIOUS_SCREEN_NAME] as! String) + XCTAssertEqual("abc123ef", screenViewEventB.attributes[Event.ReservedAttribute.PREVIOUS_SCREEN_UNIQUEID] as! String) + + XCTAssertNotNil(engagementEvent1.attributes[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP]) + XCTAssertNotNil(engagementEvent2.attributes[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP]) + XCTAssertEqual("SomeInnerView", engagementEvent2.attributes[Event.ReservedAttribute.SCREEN_NAME] as! String) + } + func testDisableSDKWillNotRecordScreenViewEvents() { clickstream.isEnable = false activityTracker.callback?(.runningInForeground) @@ -208,4 +296,35 @@ class AutoRecordEventClientTest: XCTestCase { window.makeKeyAndVisible() XCTAssertTrue(eventRecorder.saveCount == 0) } + + func testRecordTwoScreenViewWhenAutoTrackIsDisabled() { + clickstream.isEnable = false + autoRecordEventClient.setIsEntrances() + let event = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW) + event.addAttribute("HomeView1", forKey: ClickstreamAnalytics.Attr.SCREEN_NAME) + event.addAttribute("HomeView1_UniqueId", forKey: ClickstreamAnalytics.Attr.SCREEN_UNIQUE_ID) + autoRecordEventClient.recordViewScreenManually(event) + + autoRecordEventClient.updateLastScreenStartTimestamp(Date().millisecondsSince1970 - 1_100) + let event2 = clickstream.analyticsClient.createEvent(withEventType: Event.PresetEvent.SCREEN_VIEW) + event2.addAttribute("HomeView2", forKey: ClickstreamAnalytics.Attr.SCREEN_NAME) + event2.addAttribute("HomeView2_UniqueId", forKey: ClickstreamAnalytics.Attr.SCREEN_UNIQUE_ID) + autoRecordEventClient.recordViewScreenManually(event2) + + XCTAssertEqual(3, eventRecorder.savedEvents.count) + let screenView1 = eventRecorder.savedEvents[0] + let engagementEvent = eventRecorder.savedEvents[1] + let screenView2 = eventRecorder.savedEvents[2] + XCTAssertEqual(Event.PresetEvent.SCREEN_VIEW, screenView1.eventType) + XCTAssertEqual(Event.PresetEvent.USER_ENGAGEMENT, engagementEvent.eventType) + XCTAssertEqual(Event.PresetEvent.SCREEN_VIEW, screenView2.eventType) + + XCTAssertEqual("HomeView1", engagementEvent.attributes[Event.ReservedAttribute.SCREEN_NAME] as! String) + XCTAssertEqual("HomeView1_UniqueId", engagementEvent.attributes[Event.ReservedAttribute.SCREEN_UNIQUEID] as! String) + + XCTAssertEqual("HomeView2", screenView2.attributes[Event.ReservedAttribute.SCREEN_NAME] as! String) + XCTAssertEqual("HomeView2_UniqueId", screenView2.attributes[Event.ReservedAttribute.SCREEN_UNIQUEID] as! String) + XCTAssertEqual("HomeView1", screenView2.attributes[Event.ReservedAttribute.PREVIOUS_SCREEN_NAME] as! String) + XCTAssertEqual("HomeView1_UniqueId", screenView2.attributes[Event.ReservedAttribute.PREVIOUS_SCREEN_UNIQUEID] as! String) + } } diff --git a/Tests/ClickstreamTests/IntegrationTest.swift b/Tests/ClickstreamTests/IntegrationTest.swift index df55ffc..8d47431 100644 --- a/Tests/ClickstreamTests/IntegrationTest.swift +++ b/Tests/ClickstreamTests/IntegrationTest.swift @@ -98,6 +98,19 @@ class IntegrationTest: XCTestCase { XCTAssertEqual(2, eventCount) } + func testRecordCustomScreenViewEvent() throws { + ClickstreamAnalytics.recordEvent(ClickstreamAnalytics.EventName.SCREEN_VIEW, [ + ClickstreamAnalytics.Attr.SCREEN_NAME: "HomeView", + ClickstreamAnalytics.Attr.SCREEN_UNIQUE_ID: "23ac31df" + ]) + Thread.sleep(forTimeInterval: 0.2) + let event = try getEventForName(ClickstreamAnalytics.EventName.SCREEN_VIEW) + let attributes = event["attributes"] as! [String: Any] + XCTAssertEqual("HomeView", attributes[ClickstreamAnalytics.Attr.SCREEN_NAME] as! String) + XCTAssertEqual("23ac31df", attributes[ClickstreamAnalytics.Attr.SCREEN_UNIQUE_ID] as! String) + XCTAssertEqual(1, attributes[Event.ReservedAttribute.ENTRANCES] as! Int) + } + func testFlushEvents() throws { ClickstreamAnalytics.recordEvent("testEvent") Thread.sleep(forTimeInterval: 0.1) @@ -329,6 +342,18 @@ class IntegrationTest: XCTestCase { XCTAssertEqual(99.9, eventItem["price"] as! Double) } + func testRecordCustomScreenViewEventForObjc() throws { + ClickstreamObjc.recordEvent(EventName.SCREEN_VIEW, + [Attr.SCREEN_NAME: "HomeView", + Attr.SCREEN_UNIQUE_ID: "23ac31df"]) + Thread.sleep(forTimeInterval: 0.2) + let event = try getEventForName(ClickstreamAnalytics.EventName.SCREEN_VIEW) + let attributes = event["attributes"] as! [String: Any] + XCTAssertEqual("HomeView", attributes[ClickstreamAnalytics.Attr.SCREEN_NAME] as! String) + XCTAssertEqual("23ac31df", attributes[ClickstreamAnalytics.Attr.SCREEN_UNIQUE_ID] as! String) + XCTAssertEqual(1, attributes[Event.ReservedAttribute.ENTRANCES] as! Int) + } + func testGlobalAttributeForObjc() throws { let attribute: NSDictionary = [ "Channel": "SMS", @@ -402,10 +427,14 @@ class IntegrationTest: XCTestCase { } private func getTestEvent() throws -> [String: Any] { + try getEventForName("testEvent") + } + + private func getEventForName(_ name: String) throws -> [String: Any] { var testEvent: [String: Any] = JsonObject() let eventArray = try eventRecorder.getBatchEvent().eventsJson.jsonArray() for event in eventArray { - if event["event_type"] as! String == "testEvent" { + if event["event_type"] as! String == name { testEvent = event break } diff --git a/Tests/ClickstreamTests/Mock/MockAnalyticsClient.swift b/Tests/ClickstreamTests/Mock/MockAnalyticsClient.swift index ac6d8d0..cf453d9 100644 --- a/Tests/ClickstreamTests/Mock/MockAnalyticsClient.swift +++ b/Tests/ClickstreamTests/Mock/MockAnalyticsClient.swift @@ -127,7 +127,7 @@ class MockAnalyticsClient: AnalyticsClientBehaviour { var lastRecordedEvent: ClickstreamEvent? var recordedEvents: [ClickstreamEvent] = [] - func record(_ event: ClickstreamEvent) async throws { + func record(_ event: ClickstreamEvent) throws { recordCount += 1 lastRecordedEvent = event recordedEvents.append(event)