Skip to content

Commit eb441ae

Browse files
authored
feat: delete custom user attributes from events (#41)
1 parent 87a0e7f commit eb441ae

File tree

7 files changed

+70
-37
lines changed

7 files changed

+70
-37
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ Clickstream Swift SDK can help you easily collect and report in-app events from
66

77
The SDK relies on the Amplify for Swift Core Library and is developed according to the Amplify Swift SDK plug-in specification. In addition, we've added features that automatically collect common user events and attributes (e.g., screen view, first open) to simplify data collection for users.
88

9+
Visit our [Documentation site](https://awslabs.github.io/clickstream-analytics-on-aws/en/latest/sdk-manual/swift/) and to learn more about Clickstream Swift SDK.
10+
911
### Platform Support
1012

1113
The Clickstream SDK supports iOS 13+.
1214

13-
[**API Documentation**](https://awslabs.github.io/clickstream-swift/)
15+
[**API Documentation**](https://awslabs.github.io/clickstream-swift/)
1416

1517
- [Objective-C API Reference](https://awslabs.github.io/clickstream-swift/Classes/ClickstreamObjc.html)
1618

Sources/Clickstream/AWSClickstreamPlugin+ClientBehavior.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ extension AWSClickstreamPlugin {
9595

9696
func enable() {
9797
if isEnabled { return }
98-
self.autoFlushEventsTimer?.resume()
98+
autoFlushEventsTimer?.resume()
9999
clickstream.isEnable = true
100100
isEnabled = true
101101
}
@@ -104,6 +104,6 @@ extension AWSClickstreamPlugin {
104104
if !isEnabled { return }
105105
isEnabled = false
106106
clickstream.isEnable = false
107-
self.autoFlushEventsTimer?.suspend()
107+
autoFlushEventsTimer?.suspend()
108108
}
109109
}

Sources/Clickstream/ClickstreamObjc.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ import Foundation
7171
try ClickstreamAnalytics.getClickstreamConfiguration()
7272
}
7373

74-
7574
private static func getItems(_ items: [NSDictionary]) -> [ClickstreamAttribute] {
7675
var resultItems: [ClickstreamAttribute] = []
7776
for item in items {

Sources/Clickstream/Dependency/Clickstream/Analytics/AnalyticsClient.swift

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class AnalyticsClient: AnalyticsClientBehaviour {
2727
private(set) var eventRecorder: AnalyticsEventRecording
2828
private let sessionProvider: SessionProvider
2929
private(set) lazy var globalAttributes: [String: AttributeValue] = [:]
30-
private(set) var userAttributes: [String: Any] = [:]
30+
private(set) var allUserAttributes: [String: Any] = [:]
31+
private(set) var simpleUserAttributes: [String: Any] = [:]
3132
private let clickstream: ClickstreamContext
3233
private(set) var userId: String?
3334
var autoRecordClient: AutoRecordEventClient?
@@ -40,7 +41,8 @@ class AnalyticsClient: AnalyticsClientBehaviour {
4041
self.eventRecorder = eventRecorder
4142
self.sessionProvider = sessionProvider
4243
self.userId = UserDefaultsUtil.getCurrentUserId(storage: clickstream.storage)
43-
self.userAttributes = UserDefaultsUtil.getUserAttributes(storage: clickstream.storage)
44+
self.allUserAttributes = UserDefaultsUtil.getUserAttributes(storage: clickstream.storage)
45+
self.simpleUserAttributes = getSimpleUserAttributes()
4446
self.autoRecordClient = (clickstream.sessionClient as? SessionClient)?.autoRecordClient
4547
}
4648

@@ -57,9 +59,9 @@ class AnalyticsClient: AnalyticsClientBehaviour {
5759
}
5860

5961
func addUserAttribute(_ attribute: AttributeValue, forKey key: String) {
60-
let eventError = EventChecker.checkUserAttribute(
61-
currentNumber: userAttributes.count,
62-
key: key, value: attribute)
62+
let eventError = EventChecker.checkUserAttribute(currentNumber: allUserAttributes.count,
63+
key: key,
64+
value: attribute)
6365
if eventError.errorCode > 0 {
6466
recordEventError(eventError)
6567
} else {
@@ -70,7 +72,7 @@ class AnalyticsClient: AnalyticsClientBehaviour {
7072
userAttribute["value"] = attribute
7173
}
7274
userAttribute["set_timestamp"] = Date().millisecondsSince1970
73-
userAttributes[key] = userAttribute
75+
allUserAttributes[key] = userAttribute
7476
}
7577
}
7678

@@ -79,15 +81,15 @@ class AnalyticsClient: AnalyticsClientBehaviour {
7981
}
8082

8183
func removeUserAttribute(forKey key: String) {
82-
userAttributes[key] = nil
84+
allUserAttributes[key] = nil
8385
}
8486

8587
func updateUserId(_ id: String?) {
8688
if userId != id {
8789
userId = id
8890
UserDefaultsUtil.saveCurrentUserId(storage: clickstream.storage, userId: userId)
8991
if let newUserId = id, !newUserId.isEmpty {
90-
userAttributes = JsonObject()
92+
allUserAttributes = JsonObject()
9193
let userInfo = UserDefaultsUtil.getNewUserInfo(storage: clickstream.storage, userId: newUserId)
9294
// swiftlint:disable force_cast
9395
clickstream.userUniqueId = userInfo["user_unique_id"] as! String
@@ -100,11 +102,12 @@ class AnalyticsClient: AnalyticsClientBehaviour {
100102
} else {
101103
addUserAttribute(id!, forKey: Event.ReservedAttribute.USER_ID)
102104
}
105+
simpleUserAttributes = getSimpleUserAttributes()
103106
}
104107
}
105108

106109
func updateUserAttributes() {
107-
UserDefaultsUtil.updateUserAttributes(storage: clickstream.storage, userAttributes: userAttributes)
110+
UserDefaultsUtil.updateUserAttributes(storage: clickstream.storage, userAttributes: allUserAttributes)
108111
}
109112

110113
// MARK: - Event recording
@@ -140,7 +143,11 @@ class AnalyticsClient: AnalyticsClientBehaviour {
140143
forKey: Event.ReservedAttribute.SCREEN_UNIQUEID)
141144
}
142145
}
143-
event.setUserAttribute(userAttributes)
146+
if event.eventType == Event.PresetEvent.PROFILE_SET {
147+
event.setUserAttribute(allUserAttributes)
148+
} else {
149+
event.setUserAttribute(simpleUserAttributes)
150+
}
144151
try eventRecorder.save(event)
145152
}
146153

@@ -160,6 +167,17 @@ class AnalyticsClient: AnalyticsClientBehaviour {
160167
func submitEvents(isBackgroundMode: Bool = false) {
161168
eventRecorder.submitEvents(isBackgroundMode: isBackgroundMode)
162169
}
170+
171+
func getSimpleUserAttributes() -> [String: Any] {
172+
simpleUserAttributes = [:]
173+
simpleUserAttributes[Event.ReservedAttribute.USER_FIRST_TOUCH_TIMESTAMP]
174+
= allUserAttributes[Event.ReservedAttribute.USER_FIRST_TOUCH_TIMESTAMP]
175+
if allUserAttributes.keys.contains(Event.ReservedAttribute.USER_ID) {
176+
simpleUserAttributes[Event.ReservedAttribute.USER_ID]
177+
= allUserAttributes[Event.ReservedAttribute.USER_ID]
178+
}
179+
return simpleUserAttributes
180+
}
163181
}
164182

165183
extension AnalyticsClient: ClickstreamLogger {}

Sources/Clickstream/Dependency/Clickstream/Event/EventChecker.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,9 @@ class EventChecker {
211211
error.errorMessage = getErrorMessage(key)
212212
}
213213
}
214-
if error.errorCode == Event.ErrorCode.NO_ERROR, valueString.utf8.count > Event.Limit.MAX_LENGTH_OF_ITEM_VALUE {
214+
if error.errorCode == Event.ErrorCode.NO_ERROR,
215+
valueString.utf8.count > Event.Limit.MAX_LENGTH_OF_ITEM_VALUE
216+
{
215217
errorMsg = """
216218
item attribute : \(key), reached the max length of item attribute value limit (
217219
\(Event.Limit.MAX_LENGTH_OF_ITEM_VALUE). current length is: (\(valueString.utf8.count))

Tests/ClickstreamTests/Clickstream/AnalyticsClientTest.swift

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ class AnalyticsClientTest: XCTestCase {
126126

127127
func testAddUserAttributeSuccess() {
128128
analyticsClient.addUserAttribute("appStore", forKey: "userChannel")
129-
let userAttributeCount = analyticsClient.userAttributes.count
130-
let attributeValue = (analyticsClient.userAttributes["userChannel"] as! JsonObject)["value"] as? String
129+
let userAttributeCount = analyticsClient.allUserAttributes.count
130+
let attributeValue = (analyticsClient.allUserAttributes["userChannel"] as! JsonObject)["value"] as? String
131131
XCTAssertEqual(userAttributeCount, 2)
132132
XCTAssertEqual(attributeValue, "appStore")
133133
}
@@ -169,8 +169,8 @@ class AnalyticsClientTest: XCTestCase {
169169
analyticsClient.addUserAttribute("value1", forKey: "name01")
170170
analyticsClient.addUserAttribute("value2", forKey: "name02")
171171
analyticsClient.removeUserAttribute(forKey: "name01")
172-
let value1 = analyticsClient.userAttributes["name01"]
173-
let value2 = analyticsClient.userAttributes["name02"]
172+
let value1 = analyticsClient.allUserAttributes["name01"]
173+
let value2 = analyticsClient.allUserAttributes["name02"]
174174
XCTAssertNil(value1)
175175
XCTAssertNotNil(value2)
176176
}
@@ -180,7 +180,7 @@ class AnalyticsClientTest: XCTestCase {
180180
analyticsClient.addUserAttribute("value", forKey: "name\(i)")
181181
}
182182
analyticsClient.removeUserAttribute(forKey: "name1000")
183-
let userAttributeCount = analyticsClient.userAttributes.count
183+
let userAttributeCount = analyticsClient.allUserAttributes.count
184184
XCTAssertEqual(100, userAttributeCount)
185185
}
186186

@@ -201,7 +201,7 @@ class AnalyticsClientTest: XCTestCase {
201201
}
202202
Thread.sleep(forTimeInterval: 0.02)
203203
XCTAssertEqual(0, eventRecorder.saveCount)
204-
let userAttributeCount = analyticsClient.userAttributes.count
204+
let userAttributeCount = analyticsClient.allUserAttributes.count
205205
XCTAssertEqual(2, userAttributeCount)
206206
}
207207

@@ -210,7 +210,7 @@ class AnalyticsClientTest: XCTestCase {
210210
let userUniqueId = clickstream.userUniqueId
211211
XCTAssertNil(userId)
212212
XCTAssertNotNil(userUniqueId)
213-
let userAttribute = analyticsClient.userAttributes
213+
let userAttribute = analyticsClient.allUserAttributes
214214
XCTAssertTrue(userAttribute.keys.contains(Event.ReservedAttribute.USER_FIRST_TOUCH_TIMESTAMP))
215215
}
216216

@@ -220,19 +220,26 @@ class AnalyticsClientTest: XCTestCase {
220220
analyticsClient.updateUserId(userIdForA)
221221
analyticsClient.addUserAttribute(12, forKey: "user_age")
222222
analyticsClient.updateUserId(userIdForA)
223-
let userAttribute = analyticsClient.userAttributes
223+
let userAttribute = analyticsClient.allUserAttributes
224224
XCTAssertTrue(userAttribute.keys.contains("user_age"))
225225
XCTAssertEqual(userUniqueId, clickstream.userUniqueId)
226226
}
227227

228+
func testGetSimpleUserAttributeWithUserId() {
229+
analyticsClient.updateUserId("123")
230+
let simpleUserAttributes = analyticsClient.getSimpleUserAttributes()
231+
XCTAssertTrue(simpleUserAttributes.keys.contains(Event.ReservedAttribute.USER_FIRST_TOUCH_TIMESTAMP))
232+
XCTAssertTrue(simpleUserAttributes.keys.contains(Event.ReservedAttribute.USER_ID))
233+
}
234+
228235
func testUpdateDifferentUserId() {
229236
let userIdForA = "aaa"
230237
let userIdForB = "bbb"
231238
let userUniqueId = clickstream.userUniqueId
232239
analyticsClient.updateUserId(userIdForA)
233240
analyticsClient.addUserAttribute(12, forKey: "user_age")
234241
analyticsClient.updateUserId(userIdForB)
235-
let userAttribute = analyticsClient.userAttributes
242+
let userAttribute = analyticsClient.allUserAttributes
236243
XCTAssertFalse(userAttribute.keys.contains("user_age"))
237244
XCTAssertNotEqual(userUniqueId, clickstream.userUniqueId)
238245
}
@@ -284,7 +291,7 @@ class AnalyticsClientTest: XCTestCase {
284291
}
285292
}
286293

287-
func testRecordRecordEventWithUserAttribute() async {
294+
func testRecordRecordEventWithoutCustomUserAttribute() async {
288295
let event = analyticsClient.createEvent(withEventType: "testEvent")
289296
XCTAssertTrue(event.attributes.isEmpty)
290297

@@ -300,10 +307,10 @@ class AnalyticsClientTest: XCTestCase {
300307
return
301308
}
302309

303-
XCTAssertEqual(savedEvent.userAttributes.count, 4)
304-
XCTAssertEqual((savedEvent.userAttributes["attribute_0"] as! JsonObject)["value"] as? String, "test_0")
305-
XCTAssertEqual((savedEvent.userAttributes["metric_0"] as! JsonObject)["value"] as? Int, 0)
306-
XCTAssertEqual((savedEvent.userAttributes["metric_1"] as! JsonObject)["value"] as? Int, 1)
310+
XCTAssertEqual(savedEvent.userAttributes.count, 1)
311+
XCTAssertFalse(savedEvent.userAttributes.keys.contains("test_0"))
312+
XCTAssertFalse(savedEvent.userAttributes.keys.contains("metric_0"))
313+
XCTAssertFalse(savedEvent.userAttributes.keys.contains("metric_1"))
307314

308315
} catch {
309316
XCTFail("Unexpected exception while attempting to record event")

Tests/ClickstreamTests/IntegrationTest.swift

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,16 @@ class IntegrationTest: XCTestCase {
156156
Thread.sleep(forTimeInterval: 0.1)
157157
let testEvent = try getTestEvent()
158158
let userInfo = testEvent["user"] as! [String: Any]
159-
XCTAssertEqual(21, (userInfo["_user_age"] as! JsonObject)["value"] as! Int)
160-
XCTAssertEqual(true, (userInfo["isFirstOpen"] as! JsonObject)["value"] as! Bool)
161-
XCTAssertEqual(85.2, (userInfo["score"] as! JsonObject)["value"] as! Double)
162-
XCTAssertEqual("carl", (userInfo["_user_name"] as! JsonObject)["value"] as! String)
163159
XCTAssertEqual("13232", (userInfo[Event.ReservedAttribute.USER_ID] as! JsonObject)["value"] as! String)
160+
XCTAssertFalse(userInfo.keys.contains("_user_age"))
161+
XCTAssertFalse(userInfo.keys.contains("isFirstOpen"))
162+
XCTAssertFalse(userInfo.keys.contains("score"))
163+
XCTAssertFalse(userInfo.keys.contains("sc_user_nameore"))
164+
165+
XCTAssertEqual(21, (analyticsClient.allUserAttributes["_user_age"] as! JsonObject)["value"] as! Int)
166+
XCTAssertEqual(true, (analyticsClient.allUserAttributes["isFirstOpen"] as! JsonObject)["value"] as! Bool)
167+
XCTAssertEqual(85.2, ((analyticsClient.allUserAttributes["score"] as! JsonObject)["value"] as! NSDecimalNumber).doubleValue)
168+
XCTAssertEqual("carl", (analyticsClient.allUserAttributes["_user_name"] as! JsonObject)["value"] as! String)
164169
}
165170

166171
func testSetUserIdString() throws {
@@ -354,10 +359,10 @@ class IntegrationTest: XCTestCase {
354359
Thread.sleep(forTimeInterval: 0.1)
355360
let testEvent = try getTestEvent()
356361
let userInfo = testEvent["user"] as! [String: Any]
357-
XCTAssertEqual(21, (userInfo["_user_age"] as! JsonObject)["value"] as! Int)
358-
XCTAssertEqual(true, (userInfo["isFirstOpen"] as! JsonObject)["value"] as! Bool)
359-
XCTAssertEqual(85.2, (userInfo["score"] as! JsonObject)["value"] as! Double)
360-
XCTAssertEqual("carl", (userInfo["_user_name"] as! JsonObject)["value"] as! String)
362+
XCTAssertFalse(userInfo.keys.contains("_user_age"))
363+
XCTAssertFalse(userInfo.keys.contains("isFirstOpen"))
364+
XCTAssertFalse(userInfo.keys.contains("score"))
365+
XCTAssertFalse(userInfo.keys.contains("sc_user_nameore"))
361366
XCTAssertEqual("3231", (userInfo[Event.ReservedAttribute.USER_ID] as! JsonObject)["value"] as! String)
362367
}
363368

0 commit comments

Comments
 (0)