From 712a4a6f44590c2472c95cbe7e2aac833c1e5faf Mon Sep 17 00:00:00 2001 From: xiaoweii Date: Wed, 10 Apr 2024 17:55:45 +0800 Subject: [PATCH 1/3] feat: add preset traffic source attributes --- README.md | 13 +++- .../Clickstream/ClickstreamAnalytics.swift | 24 +++++++ Sources/Clickstream/ClickstreamObjc.swift | 22 +++++++ Tests/ClickstreamTests/IntegrationTest.swift | 65 ++++++++++++++++++- 4 files changed, 120 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 218e057..47965d6 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,8 @@ func application(_ application: UIApplication, didFinishLaunchingWithOptions lau #### 3.2 Initialize the SDK with global attributes and custom configuration +The following example code shows how to add traffic source fields as global attributes when initializing the SDK. + ```swift import Clickstream ... @@ -92,8 +94,15 @@ func application(_ application: UIApplication, didFinishLaunchingWithOptions lau .withEndpoint("https://example.com/collect") .withLogEvents(true) .withInitialGlobalAttributes([ - "_traffic_source_name": "Summer promotion", - "_traffic_source_medium": "Search engine" + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_SOURCE: "amazon", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_MEDIUM: "cpc", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CAMPAIGN: "summer_promotion", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CAMPAIGN_ID: "summer_promotion_01", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_TERM: "running_shoes", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CONTENT: "banner_ad_1", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CLID: "amazon_ad_123", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CLID_PLATFORM: "amazon_ads", + ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL: "App Store" ]) try ClickstreamAnalytics.initSDK(configuration) } catch { diff --git a/Sources/Clickstream/ClickstreamAnalytics.swift b/Sources/Clickstream/ClickstreamAnalytics.swift index 03e17eb..f32c9fc 100644 --- a/Sources/Clickstream/ClickstreamAnalytics.swift +++ b/Sources/Clickstream/ClickstreamAnalytics.swift @@ -113,8 +113,32 @@ public enum ClickstreamAnalytics { /// ClickstreamANalytics preset attributes public enum Attr { + /// 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" + /// Preset attribute traffic source source + public static let TRAFFIC_SOURCE_SOURCE = "_traffic_source_source" + /// Preset attribute traffic source medium + public static let TRAFFIC_SOURCE_MEDIUM = "_traffic_source_medium" + /// Preset attribute traffic source campaign + public static let TRAFFIC_SOURCE_CAMPAIGN = "_traffic_source_campaign" + /// Preset attribute traffic source campaign id + public static let TRAFFIC_SOURCE_CAMPAIGN_ID = "_traffic_source_campaign_id" + /// Preset attribute traffic source term + public static let TRAFFIC_SOURCE_TERM = "_traffic_source_term" + /// Preset attribute traffic source content + public static let TRAFFIC_SOURCE_CONTENT = "_traffic_source_content" + /// Preset attribute traffic source clid + public static let TRAFFIC_SOURCE_CLID = "_traffic_source_clid" + /// Preset attribute traffic source clid platform + public static let TRAFFIC_SOURCE_CLID_PLATFORM = "_traffic_source_clid_platform" + /// Preset attribute app install channel + public static let APP_INSTALL_CHANNEL = "_app_install_channel" + /// Preset attribute event value + public static let VALUE = "_value" + /// Preset attribute event currency + public static let CURRENCY = "_currency" } /// ClickstreamAnalytics preset item attributes diff --git a/Sources/Clickstream/ClickstreamObjc.swift b/Sources/Clickstream/ClickstreamObjc.swift index d00b6fb..f2cfd62 100644 --- a/Sources/Clickstream/ClickstreamObjc.swift +++ b/Sources/Clickstream/ClickstreamObjc.swift @@ -134,6 +134,28 @@ import Foundation public static let SCREEN_NAME = "_screen_name" /// Preset attribute screen unique id public static let SCREEN_UNIQUE_ID = "_screen_unique_id" + /// Preset attribute traffic source source + public static let TRAFFIC_SOURCE_SOURCE = "_traffic_source_source" + /// Preset attribute traffic source medium + public static let TRAFFIC_SOURCE_MEDIUM = "_traffic_source_medium" + /// Preset attribute traffic source campaign + public static let TRAFFIC_SOURCE_CAMPAIGN = "_traffic_source_campaign" + /// Preset attribute traffic source campaign id + public static let TRAFFIC_SOURCE_CAMPAIGN_ID = "_traffic_source_campaign_id" + /// Preset attribute traffic source term + public static let TRAFFIC_SOURCE_TERM = "_traffic_source_term" + /// Preset attribute traffic source content + public static let TRAFFIC_SOURCE_CONTENT = "_traffic_source_content" + /// Preset attribute traffic source clid + public static let TRAFFIC_SOURCE_CLID = "_traffic_source_clid" + /// Preset attribute traffic source clid platform + public static let TRAFFIC_SOURCE_CLID_PLATFORM = "_traffic_source_clid_platform" + /// Preset attribute app install channel + public static let APP_INSTALL_CHANNEL = "_app_install_channel" + /// Preset attribute event value + public static let VALUE = "_value" + /// Preset attribute event currency + public static let CURRENCY = "_currency" } /// ClickstreamAnalytics preset item keys for objective-c diff --git a/Tests/ClickstreamTests/IntegrationTest.swift b/Tests/ClickstreamTests/IntegrationTest.swift index fd2797b..dba9482 100644 --- a/Tests/ClickstreamTests/IntegrationTest.swift +++ b/Tests/ClickstreamTests/IntegrationTest.swift @@ -77,7 +77,11 @@ class IntegrationTest: XCTestCase { ClickstreamAnalytics.Item.ITEM_CATEGORY: "book", ClickstreamAnalytics.Item.PRICE: 99.9 ] - ClickstreamAnalytics.recordEvent("testEvent", ["id": 123], [item_book]) + ClickstreamAnalytics.recordEvent("testEvent", + ["id": 123, + ClickstreamAnalytics.Attr.VALUE: 99.9, + ClickstreamAnalytics.Attr.CURRENCY: "USD"], + [item_book]) Thread.sleep(forTimeInterval: 0.2) let testEvent = try getTestEvent() let items = testEvent["items"] as! [JsonObject] @@ -136,6 +140,33 @@ class IntegrationTest: XCTestCase { XCTAssertEqual(true, eventAttribute["isOpenNotification"] as! Bool) } + func testAddGlobalAttributeForTrafficSource() throws { + ClickstreamAnalytics.addGlobalAttributes([ + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_SOURCE: "amazon", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_MEDIUM: "cpc", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CAMPAIGN: "summer_promotion", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CAMPAIGN_ID: "summer_promotion_01", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_TERM: "running_shoes", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CONTENT: "banner_ad_1", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CLID: "amazon_ad_123", + ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CLID_PLATFORM: "amazon_ads", + ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL: "App Store" + ]) + ClickstreamAnalytics.recordEvent("testEvent") + Thread.sleep(forTimeInterval: 0.1) + let testEvent = try getTestEvent() + let eventAttribute = testEvent["attributes"] as! [String: Any] + XCTAssertEqual("amazon", eventAttribute[ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_SOURCE] as! String) + XCTAssertEqual("cpc", eventAttribute[ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_MEDIUM] as! String) + XCTAssertEqual("summer_promotion", eventAttribute[ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CAMPAIGN] as! String) + XCTAssertEqual("summer_promotion_01", eventAttribute[ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CAMPAIGN_ID] as! String) + XCTAssertEqual("running_shoes", eventAttribute[ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_TERM] as! String) + XCTAssertEqual("banner_ad_1", eventAttribute[ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CONTENT] as! String) + XCTAssertEqual("amazon_ad_123", eventAttribute[ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CLID] as! String) + XCTAssertEqual("amazon_ads", eventAttribute[ClickstreamAnalytics.Attr.TRAFFIC_SOURCE_CLID_PLATFORM] as! String) + XCTAssertEqual("App Store", eventAttribute[ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL] as! String) + } + func testDeleteGlobalAttribute() throws { ClickstreamAnalytics.addGlobalAttributes([ "channel": "AppStore", @@ -326,7 +357,9 @@ class IntegrationTest: XCTestCase { "event_category": "recommended" ] ClickstreamObjc.recordEvent("testEvent", - ["id": 123], + ["id": 123, + Attr.VALUE: 99.9, + Attr.CURRENCY: "USD"], [item]) Thread.sleep(forTimeInterval: 0.2) let testEvent = try getTestEvent() @@ -371,6 +404,34 @@ class IntegrationTest: XCTestCase { XCTAssertEqual(true, eventAttribute["Successful"] as! Bool) } + func testAddTrafficSourceForObjc() throws { + let attribute: NSDictionary = [ + Attr.TRAFFIC_SOURCE_SOURCE: "amazon", + Attr.TRAFFIC_SOURCE_MEDIUM: "cpc", + Attr.TRAFFIC_SOURCE_CAMPAIGN: "summer_promotion", + Attr.TRAFFIC_SOURCE_CAMPAIGN_ID: "summer_promotion_01", + Attr.TRAFFIC_SOURCE_TERM: "running_shoes", + Attr.TRAFFIC_SOURCE_CONTENT: "banner_ad_1", + Attr.TRAFFIC_SOURCE_CLID: "amazon_ad_123", + Attr.TRAFFIC_SOURCE_CLID_PLATFORM: "amazon_ads", + Attr.APP_INSTALL_CHANNEL: "App Store" + ] + ClickstreamObjc.addGlobalAttributes(attribute) + ClickstreamObjc.recordEvent("testEvent") + Thread.sleep(forTimeInterval: 0.1) + let testEvent = try getTestEvent() + let eventAttribute = testEvent["attributes"] as! [String: Any] + XCTAssertNotNil(eventAttribute[Attr.TRAFFIC_SOURCE_SOURCE]) + XCTAssertNotNil(eventAttribute[Attr.TRAFFIC_SOURCE_MEDIUM]) + XCTAssertNotNil(eventAttribute[Attr.TRAFFIC_SOURCE_CAMPAIGN]) + XCTAssertNotNil(eventAttribute[Attr.TRAFFIC_SOURCE_CAMPAIGN_ID]) + XCTAssertNotNil(eventAttribute[Attr.TRAFFIC_SOURCE_TERM]) + XCTAssertNotNil(eventAttribute[Attr.TRAFFIC_SOURCE_CONTENT]) + XCTAssertNotNil(eventAttribute[Attr.TRAFFIC_SOURCE_CLID]) + XCTAssertNotNil(eventAttribute[Attr.TRAFFIC_SOURCE_CLID_PLATFORM]) + XCTAssertNotNil(eventAttribute[Attr.APP_INSTALL_CHANNEL]) + } + func testUserAttributeForObjc() throws { ClickstreamObjc.setUserId("3231") let userAttribute: NSDictionary = [ From ad9861db35300e669fa3d83b36f4316b1a458ac8 Mon Sep 17 00:00:00 2001 From: xiaoweii Date: Wed, 10 Apr 2024 18:06:35 +0800 Subject: [PATCH 2/3] chore: change channel to preset attribute --- Tests/ClickstreamTests/IntegrationTest.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/ClickstreamTests/IntegrationTest.swift b/Tests/ClickstreamTests/IntegrationTest.swift index dba9482..9825c1b 100644 --- a/Tests/ClickstreamTests/IntegrationTest.swift +++ b/Tests/ClickstreamTests/IntegrationTest.swift @@ -124,7 +124,7 @@ class IntegrationTest: XCTestCase { func testAddGlobalAttribute() throws { ClickstreamAnalytics.addGlobalAttributes([ - "channel": "AppStore", + ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL: "App Store", "level": 5.1, "class": 5, "isOpenNotification": true @@ -134,7 +134,7 @@ class IntegrationTest: XCTestCase { let testEvent = try getTestEvent() let eventAttribute = testEvent["attributes"] as! [String: Any] - XCTAssertEqual("AppStore", eventAttribute["channel"] as! String) + XCTAssertEqual("AppStore", eventAttribute[ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL] as! String) XCTAssertEqual(5.1, eventAttribute["level"] as! Double) XCTAssertEqual(5, eventAttribute["class"] as! Int) XCTAssertEqual(true, eventAttribute["isOpenNotification"] as! Bool) @@ -169,18 +169,18 @@ class IntegrationTest: XCTestCase { func testDeleteGlobalAttribute() throws { ClickstreamAnalytics.addGlobalAttributes([ - "channel": "AppStore", + ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL: "App Store", "level": 5.1, "class": 5, "isOpenNotification": true ]) - ClickstreamAnalytics.deleteGlobalAttributes("channel") + ClickstreamAnalytics.deleteGlobalAttributes(ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL) ClickstreamAnalytics.recordEvent("testEvent") Thread.sleep(forTimeInterval: 0.1) let testEvent = try getTestEvent() let eventAttribute = testEvent["attributes"] as! [String: Any] - XCTAssertNil(eventAttribute["channel"]) + XCTAssertNil(eventAttribute[ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL]) XCTAssertEqual(5.1, eventAttribute["level"] as! Double) XCTAssertEqual(5, eventAttribute["class"] as! Int) XCTAssertEqual(true, eventAttribute["isOpenNotification"] as! Bool) From 78473c23d9dfedfda6ed9bb61ad30d6b5b87f32b Mon Sep 17 00:00:00 2001 From: xiaoweii Date: Wed, 10 Apr 2024 18:21:36 +0800 Subject: [PATCH 3/3] chore: update test case add code coverage token --- .github/workflows/test.yml | 3 ++- Tests/ClickstreamTests/Clickstream/EventRecorderTest.swift | 2 +- Tests/ClickstreamTests/IntegrationTest.swift | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0b19e8b..3a31ffd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,8 +22,9 @@ jobs: cd ../../../../ xcrun llvm-cov export -format="lcov" -instr-profile $pathCoverage .build/Build/Products/Debug-iphonesimulator/Clickstream.o > .build/info.lcov - name: Upload Test Report - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: + token: ${{ secrets.CODECOV_TOKEN }} name: report files: .build/info.lcov swift: true diff --git a/Tests/ClickstreamTests/Clickstream/EventRecorderTest.swift b/Tests/ClickstreamTests/Clickstream/EventRecorderTest.swift index 69e6d7e..29d0005 100644 --- a/Tests/ClickstreamTests/Clickstream/EventRecorderTest.swift +++ b/Tests/ClickstreamTests/Clickstream/EventRecorderTest.swift @@ -359,7 +359,7 @@ class EventRecorderTest: XCTestCase { eventRecorder.submitEvents() XCTAssertEqual(1, eventRecorder.queue.operationCount) - Thread.sleep(forTimeInterval: 0.5) + Thread.sleep(forTimeInterval: 0.8) let totalEvent = try dbUtil.getEventCount() XCTAssertEqual(0, totalEvent) XCTAssertTrue(eventRecorder.bundleSequenceId == 3) diff --git a/Tests/ClickstreamTests/IntegrationTest.swift b/Tests/ClickstreamTests/IntegrationTest.swift index 9825c1b..01abbf2 100644 --- a/Tests/ClickstreamTests/IntegrationTest.swift +++ b/Tests/ClickstreamTests/IntegrationTest.swift @@ -134,7 +134,7 @@ class IntegrationTest: XCTestCase { let testEvent = try getTestEvent() let eventAttribute = testEvent["attributes"] as! [String: Any] - XCTAssertEqual("AppStore", eventAttribute[ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL] as! String) + XCTAssertEqual("App Store", eventAttribute[ClickstreamAnalytics.Attr.APP_INSTALL_CHANNEL] as! String) XCTAssertEqual(5.1, eventAttribute["level"] as! Double) XCTAssertEqual(5, eventAttribute["class"] as! Int) XCTAssertEqual(true, eventAttribute["isOpenNotification"] as! Bool)