Skip to content

Commit 8654aa6

Browse files
zhu-xiaoweizxkane
authored andcommitted
feat: event record with attribute and limit
1 parent b3af70d commit 8654aa6

37 files changed

+2437
-28
lines changed

Package.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ let package = Package(
1010
products: [
1111
.library(
1212
name: "Clickstream",
13-
targets: ["Clickstream"]),
13+
targets: ["Clickstream"])
1414
],
1515
dependencies: [
16-
.package(url: "https://github.com/aws-amplify/amplify-swift.git", exact: "2.4.0"),
16+
.package(url: "https://github.com/aws-amplify/amplify-swift.git", exact: "2.4.0")
1717
],
1818
targets: [
1919
.target(
2020
name: "Clickstream",
2121
dependencies: [
22-
.product(name: "AWSPluginsCore", package: "amplify-swift"),
22+
.product(name: "AWSPluginsCore", package: "amplify-swift")
2323
]),
2424
.testTarget(
2525
name: "ClickstreamTests",
26-
dependencies: ["Clickstream"]),
26+
dependencies: ["Clickstream"])
2727
])

Sources/Clickstream/AWSClickstreamPlugin+ClientBehavior.swift

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,79 @@ import Amplify
99
import Foundation
1010

1111
public extension AWSClickstreamPlugin {
12-
func identifyUser(userId: String, userProfile: AnalyticsUserProfile?) {}
12+
func identifyUser(userId: String, userProfile: AnalyticsUserProfile?) {
13+
Task {
14+
if userId == Event.User.USER_ID_NIL {
15+
await analyticsClient.removeUserAttribute(forKey: userId)
16+
} else if userId != Event.User.USER_ID_EMPTY {
17+
await analyticsClient.addUserAttribute(userId, forKey: Event.ReservedAttribute.USER_ID)
18+
}
19+
}
20+
userProfile?.properties?.forEach { key, value in
21+
Task {
22+
await analyticsClient.addUserAttribute(value, forKey: key)
23+
}
24+
}
25+
}
1326

14-
func record(event: AnalyticsEvent) {}
27+
func record(event: AnalyticsEvent) {
28+
if !isEnabled {
29+
log.warn("Cannot record events. Clickstream is disabled")
30+
return
31+
}
32+
let clickstreamEvent = analyticsClient.createEvent(withEventType: event.name)
33+
if let properties = event.properties {
34+
clickstreamEvent.addProperties(properties)
35+
}
1536

16-
func record(eventWithName eventName: String) {}
37+
Task {
38+
do {
39+
try await analyticsClient.record(clickstreamEvent)
40+
} catch {
41+
log.error("Record event error:\(error)")
42+
}
43+
}
44+
}
1745

18-
func registerGlobalProperties(_ properties: AnalyticsProperties) {}
46+
func record(eventWithName eventName: String) {
47+
let event = BasicAnalyticsEvent(name: eventName)
48+
record(event: event)
49+
}
1950

20-
func unregisterGlobalProperties(_ keys: Set<String>?) {}
51+
func registerGlobalProperties(_ properties: AnalyticsProperties) {
52+
properties.forEach { key, value in
53+
Task {
54+
await analyticsClient.addGlobalAttribute(value, forKey: key)
55+
}
56+
}
57+
}
2158

22-
func flushEvents() {}
59+
func unregisterGlobalProperties(_ keys: Set<String>?) {
60+
keys?.forEach { key in
61+
Task {
62+
await analyticsClient.removeGlobalAttribute(forKey: key)
63+
}
64+
}
65+
}
66+
67+
func flushEvents() {
68+
if !isEnabled {
69+
log.warn("Cannot flushEvents. Clickstream is disabled")
70+
return
71+
}
72+
// Do not attempt to submit events if we detect the device is offline, as it's gonna fail anyway
73+
guard networkMonitor.isOnline else {
74+
log.error("Device is offline, skipping submitting events to Clickstream server")
75+
return
76+
}
77+
Task {
78+
try await analyticsClient.submitEvents()
79+
}
80+
}
81+
82+
func getEscapeHatch() -> ClickstreamContextConfiguration {
83+
clickstream.configuration
84+
}
2385

2486
func enable() {
2587
isEnabled = true

Sources/Clickstream/AWSClickstreamPlugin+Configure.swift

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import Amplify
99
import Foundation
10+
import Network
1011

1112
public extension AWSClickstreamPlugin {
1213
/// called when sdk init.
@@ -22,5 +23,68 @@ public extension AWSClickstreamPlugin {
2223
}
2324

2425
/// Configure AWSClickstreamPlugin programatically using AWSClickstreamConfiguration
25-
func configure(using configuration: AWSClickstreamConfiguration) throws {}
26+
func configure(using configuration: AWSClickstreamConfiguration) throws {
27+
let contextConfiguration = ClickstreamContextConfiguration(appId: configuration.appId,
28+
endpoint: configuration.endpoint,
29+
sendEventsInterval: configuration.sendEventsInterval,
30+
isTrackAppExceptionEvents: configuration.isTrackAppExceptionEvents,
31+
isCompressEvents: configuration.isCompressEvents)
32+
let clickstream = try ClickstreamContext(with: contextConfiguration)
33+
34+
let sessionClient = SessionClient(configuration: .init(uniqueDeviceId: clickstream.uniqueId,
35+
sessionBackgroundTimeout: TimeInterval(10)),
36+
userDefaults: clickstream.storage.userDefaults)
37+
clickstream.sessionClient = sessionClient
38+
let sessionProvider: () -> Session = { [weak sessionClient] in
39+
guard let sessionClient else {
40+
fatalError("SessionClient was deallocated")
41+
}
42+
return sessionClient.currentSession
43+
}
44+
let eventRecorder = try EventRecorder(clickstream: clickstream)
45+
analyticsClient = try AnalyticsClient(clickstream: clickstream,
46+
eventRecorder: eventRecorder,
47+
sessionProvider: sessionProvider)
48+
clickstream.analyticsClient = analyticsClient
49+
sessionClient.analyticsClient = analyticsClient
50+
let networkMonitor = NWPathMonitor()
51+
clickstream.networkMonitor = networkMonitor
52+
53+
var autoFlushEventsTimer: DispatchSourceTimer?
54+
if configuration.sendEventsInterval != 0 {
55+
let timeInterval = TimeInterval(configuration.sendEventsInterval / 1000)
56+
autoFlushEventsTimer = RepeatingTimer.createRepeatingTimer(
57+
timeInterval: timeInterval,
58+
eventHandler: { [weak self] in
59+
self?.log.debug("AutoFlushTimer triggered, flushing events")
60+
self?.flushEvents()
61+
}
62+
)
63+
}
64+
65+
configure(clickstream: clickstream,
66+
autoFlushEventsTimer: autoFlushEventsTimer,
67+
networkMonitor: networkMonitor)
68+
sessionClient.startSession()
69+
log.debug("init the sdk success")
70+
}
71+
72+
// MARK: Internal
73+
74+
/// Internal configure method to set the properties of the plugin
75+
internal func configure(clickstream: ClickstreamContext,
76+
autoFlushEventsTimer: DispatchSourceTimer?,
77+
networkMonitor: NetworkMonitor)
78+
{
79+
self.clickstream = clickstream
80+
isEnabled = true
81+
self.autoFlushEventsTimer = autoFlushEventsTimer
82+
self.autoFlushEventsTimer?.resume()
83+
self.networkMonitor = networkMonitor
84+
self.networkMonitor.startMonitoring(
85+
using: DispatchQueue(
86+
label: "com.amazonaws.solution.clickstream.AnalyticsPlugin.NetworkMonitor"
87+
)
88+
)
89+
}
2690
}

Sources/Clickstream/AWSClickstreamPlugin+DefaultLogger.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@
66
//
77

88
import Amplify
9-
import Foundation
109

1110
extension AWSClickstreamPlugin: DefaultLogger {}

Sources/Clickstream/AWSClickstreamPlugin+Reset.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
// SPDX-License-Identifier: Apache-2.0
66
//
77

8-
import Foundation
9-
108
public extension AWSClickstreamPlugin {
11-
func reset() async {
9+
func reset() {
10+
if clickstream != nil {
11+
clickstream = nil
12+
}
13+
1214
if autoFlushEventsTimer != nil {
1315
autoFlushEventsTimer?.setEventHandler {}
1416
autoFlushEventsTimer?.cancel()

Sources/Clickstream/AWSClickstreamPlugin.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,28 @@
66
//
77

88
import Amplify
9-
import AWSPluginsCore
109
import Foundation
1110

1211
public final class AWSClickstreamPlugin: AnalyticsCategoryPlugin {
12+
var clickstream: ClickstreamContext!
13+
1314
/// Automatically flushes the events that have been recorded on an interval
1415
var autoFlushEventsTimer: DispatchSourceTimer?
15-
16+
1617
/// An observer to monitor connectivity changes
1718
var networkMonitor: NetworkMonitor!
18-
19+
1920
/// Specifies whether the plugin is enabled
2021
var isEnabled: Bool!
21-
22+
23+
/// ClickstreamClient for evnet handle
24+
var analyticsClient: AnalyticsClientBehaviour!
25+
2226
/// The configuration file plugin key of clickstream plugin
2327
public var key: PluginKey {
2428
"awsClickstreamPlugin"
2529
}
26-
30+
2731
/// Instantiates an instance of the AWSClickstreamPlugin
2832
public init() {}
2933
}

Sources/Clickstream/ClickstreamAnalytics.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,59 @@ public enum ClickstreamAnalytics {
1313
try Amplify.add(plugin: AWSClickstreamPlugin())
1414
try Amplify.configure()
1515
}
16+
17+
/// Use this method to record Event.
18+
/// - Parameter event: ClickstreamEvent to record
19+
public static func recordEvent(event: AnalyticsEvent) {
20+
Amplify.Analytics.record(event: event)
21+
}
22+
23+
/// Use this method to record Event.
24+
/// - Parameter eventName: the event name
25+
public static func recordEvent(eventName: String) {
26+
Amplify.Analytics.record(eventWithName: eventName)
27+
}
28+
29+
/// Use this method to send events immediately.
30+
public static func flushEvents() {
31+
Amplify.Analytics.flushEvents()
32+
}
33+
34+
/// Add global attributes
35+
/// - Parameter attributes: the global attributes to add
36+
public static func addGlobalAttributes(attributes: ClickstreamAttribute) {
37+
Amplify.Analytics.registerGlobalProperties(attributes)
38+
}
39+
40+
/// Delete global attributes
41+
/// - Parameter attributes: the global attributes to delete
42+
public static func deleteGlobalAttributes(attributes: String...) {
43+
Amplify.Analytics.unregisterGlobalProperties(attributes)
44+
}
45+
46+
/// Add user attributes
47+
/// - Parameter attributes: the user attributes to add
48+
public static func addUserAttributes(userAttributes: ClickstreamUserAttribute) {
49+
let userId: String = userAttributes.userId ?? Event.User.USER_ID_EMPTY
50+
let userProfile = AnalyticsUserProfile(location: nil, properties: userAttributes.attribute)
51+
Amplify.Analytics.identifyUser(userId: userId,
52+
userProfile: userProfile)
53+
}
54+
55+
/// Set user id for login and logout
56+
/// - Parameter userId: current userId, nil for logout
57+
public static func setUserId(userId: String?) {
58+
if userId == nil {
59+
Amplify.Analytics.identifyUser(userId: Event.User.USER_ID_NIL)
60+
} else {
61+
Amplify.Analytics.identifyUser(userId: userId!)
62+
}
63+
}
64+
65+
/// Get clickstream configuration, please config it after initialize.
66+
/// - Returns: ClickstreamContextConfiguration: current userId, nil for logout
67+
public static func getClickStreamConfiguration() throws -> ClickstreamContextConfiguration? {
68+
let plugin = try Amplify.Analytics.getPlugin(for: "awsClickstreamPlugin")
69+
return (plugin as? AWSClickstreamPlugin)?.getEscapeHatch()
70+
}
1671
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Zhu, Xiaowei on 2023/3/4.
6+
//
7+
8+
import Amplify
9+
10+
public typealias ClickstreamAttribute = AnalyticsProperties
11+
public typealias BaseClickstreamEvent = BasicAnalyticsEvent
12+
public typealias AttributeValue = AnalyticsPropertyValue
13+
14+
public struct ClickstreamUserAttribute {
15+
public var userId: String?
16+
public var attribute: ClickstreamAttribute?
17+
public init(userId: String?, attribute: ClickstreamAttribute?) {
18+
self.userId = userId
19+
self.attribute = attribute
20+
}
21+
}

Sources/Clickstream/Configuration/AWSClickstreamPluginConfiguration.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@
66
//
77

88
import Amplify
9-
import AWSClientRuntime
10-
import Foundation
119

1210
public struct AWSClickstreamConfiguration {
1311
static let appIdKey = "appId"
1412
static let endpointKey = "endpoint"
1513
static let sendEventsIntervalKey = "autoFlushEventsInterval"
16-
static let trackAppExceptionKey = "isTrackAppExceptionEvents"
14+
static let isTrackAppExceptionKey = "isTrackAppExceptionEvents"
1715
static let isCompressEventsKey = "isCompressEvents"
1816

1917
static let defaultSendEventsInterval = 10000
@@ -133,7 +131,7 @@ public struct AWSClickstreamConfiguration {
133131
}
134132

135133
private static func getIsTrackAppExceptionEvents(_ configuration: [String: JSONValue]) throws -> Bool {
136-
guard let isTrackAppException = configuration[trackAppExceptionKey] else {
134+
guard let isTrackAppException = configuration[isTrackAppExceptionKey] else {
137135
return AWSClickstreamConfiguration.defaulTrackAppException
138136
}
139137

0 commit comments

Comments
 (0)