Skip to content

Commit f5ad559

Browse files
committed
🔧 Separated user update logic from regular user events saved in local storage
1 parent fb972c8 commit f5ad559

File tree

5 files changed

+100
-76
lines changed

5 files changed

+100
-76
lines changed

‎swift-sdk/Internal/AnonymousUserManager.swift

Lines changed: 93 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -92,44 +92,26 @@ public class AnonymousUserManager: AnonymousUserManagerProtocol {
9292
anonSessions[JsonKey.matchedCriteriaId] = Int(criteriaId)
9393
let appName = Bundle.main.appPackageName ?? ""
9494
notificationStateProvider.isNotificationsEnabled { isEnabled in
95-
if (!appName.isEmpty && isEnabled) {
95+
if !appName.isEmpty && isEnabled {
9696
anonSessions[JsonKey.mobilePushOptIn] = appName
9797
}
98-
99-
// store last update user event
100-
var updateUserEventIndex : Int?
101-
var dataFields: [AnyHashable:Any]?
102-
if let events = self.localStorage.anonymousUserEvents {
103-
// if there is an update user event, find the index of the last one
104-
if let eventIndex = events.lastIndex(where: { dict in
105-
if let eventType = dict[JsonKey.eventType] as? String, eventType == EventType.updateUser {
106-
return true
107-
}
108-
return false
109-
}) {
110-
updateUserEventIndex = eventIndex
111-
var updateUserEvent = events[eventIndex]
112-
updateUserEvent.removeValue(forKey: JsonKey.eventType)
113-
//save update user event to data fields removing the event type
114-
dataFields = updateUserEvent
115-
}
116-
}
11798

11899
//track anon session for new user
119-
IterableAPI.implementation?.apiClient.trackAnonSession(createdAt: IterableUtil.secondsFromEpoch(for: self.dateProvider.currentDate), withUserId: userId, dataFields: dataFields,requestJson: anonSessions).onError { error in
120-
if (error.httpStatusCode == 409) {
100+
IterableAPI.implementation?.apiClient.trackAnonSession(
101+
createdAt: IterableUtil.secondsFromEpoch(for: self.dateProvider.currentDate),
102+
withUserId: userId,
103+
dataFields: self.localStorage.anonymousUserUpdate,
104+
requestJson: anonSessions
105+
).onError { error in
106+
if error.httpStatusCode == 409 {
121107
self.getAnonCriteria() // refetch the criteria
122108
}
123109
}.onSuccess { success in
124-
//remove the update user event from local storage
125-
if var events = self.localStorage.anonymousUserEvents, let index = updateUserEventIndex {
126-
events.remove(at: index)
127-
self.localStorage.anonymousUserEvents = events
128-
}
129-
130110
self.localStorage.userIdAnnon = userId
131111
self.config.anonUserDelegate?.onAnonUserCreated(userId: userId)
132-
IterableAPI.implementation?.setUserId(userId, authToken: nil, successHandler: nil, failureHandler: nil, isAnon: true, identityResolution: nil)
112+
113+
IterableAPI.implementation?.setUserId(userId, isAnon: true)
114+
133115
self.syncNonSyncedEvents()
134116
}
135117
}
@@ -144,37 +126,32 @@ public class AnonymousUserManager: AnonymousUserManagerProtocol {
144126

145127
// Syncs locally saved data through track APIs
146128
public func syncEvents() {
147-
let events = localStorage.anonymousUserEvents
148-
var successfulSyncedData: [Int] = []
149-
150-
if let _events = events {
151-
for var eventData in _events {
129+
if let events = localStorage.anonymousUserEvents {
130+
for var eventData in events {
152131
if let eventType = eventData[JsonKey.eventType] as? String {
153132
eventData.removeValue(forKey: JsonKey.eventType)
154133
switch eventType {
155134
case EventType.customEvent:
156-
IterableAPI.implementation?.track(eventData[JsonKey.eventName] as? String ?? "", withBody: eventData, onSuccess: {result in
157-
successfulSyncedData.append(eventData[JsonKey.eventTimeStamp] as? Int ?? 0)
158-
})
135+
IterableAPI.implementation?.track(eventData[JsonKey.eventName] as? String ?? "", withBody: eventData)
159136
break
160137
case EventType.purchase:
161138
var total = NSNumber(value: 0)
162139
if let _total = NumberFormatter().number(from: eventData[JsonKey.Commerce.total] as! String) {
163140
total = _total
164141
}
165142

166-
IterableAPI.implementation?.trackPurchase(total, items: convertCommerceItems(from: eventData[JsonKey.Commerce.items] as! [[AnyHashable: Any]]), dataFields: eventData[JsonKey.dataFields] as? [AnyHashable : Any], createdAt: eventData[JsonKey.Body.createdAt] as? Int ?? 0, onSuccess: {result in
167-
successfulSyncedData.append(eventData[JsonKey.eventTimeStamp] as? Int ?? 0)
168-
})
143+
IterableAPI.implementation?.trackPurchase(
144+
total,
145+
items: convertCommerceItems(from: eventData[JsonKey.Commerce.items] as! [[AnyHashable: Any]]),
146+
dataFields: eventData[JsonKey.dataFields] as? [AnyHashable : Any],
147+
createdAt: eventData[JsonKey.Body.createdAt] as? Int ?? 0
148+
)
169149
break
170150
case EventType.updateCart:
171-
IterableAPI.implementation?.updateCart(items: convertCommerceItems(from: eventData[JsonKey.Commerce.items] as! [[AnyHashable: Any]]), createdAt: eventData[JsonKey.Body.createdAt] as? Int ?? 0,
172-
onSuccess: {result in
173-
successfulSyncedData.append(eventData[JsonKey.eventTimeStamp] as? Int ?? 0)
174-
})
175-
break
176-
case EventType.updateUser:
177-
IterableAPI.implementation?.updateUser(eventData, mergeNestedObjects: false)
151+
IterableAPI.implementation?.updateCart(
152+
items: convertCommerceItems(from: eventData[JsonKey.Commerce.items] as! [[AnyHashable: Any]]),
153+
createdAt: eventData[JsonKey.Body.createdAt] as? Int ?? 0
154+
)
178155
break
179156
default:
180157
break
@@ -193,15 +170,36 @@ public class AnonymousUserManager: AnonymousUserManagerProtocol {
193170
localStorage.anonymousUserEvents = nil
194171
localStorage.anonymousSessions = nil
195172
}
173+
174+
if var userUpdate = localStorage.anonymousUserUpdate {
175+
if let eventType = userUpdate[JsonKey.eventType] as? String {
176+
userUpdate.removeValue(forKey: JsonKey.eventType)
177+
}
178+
179+
IterableAPI.implementation?.updateUser(userUpdate, mergeNestedObjects: false)
180+
181+
localStorage.anonymousUserUpdate = nil
182+
}
183+
196184
}
197185

198186
// Checks if criterias are being met and returns criteriaId if it matches the criteria.
199187
private func evaluateCriteriaAndReturnID() -> String? {
200-
guard let events = localStorage.anonymousUserEvents, let criteriaData = localStorage.criteriaData else {
201-
return nil
188+
guard let criteriaData = localStorage.criteriaData else { return nil }
189+
190+
var events: [[AnyHashable: Any]]?
191+
192+
if let anonymousUserEvents = localStorage.anonymousUserEvents {
193+
events?.append(contentsOf: anonymousUserEvents)
194+
}
195+
196+
if let userUpdate = localStorage.anonymousUserUpdate {
197+
events?.append(userUpdate)
202198
}
203-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: criteriaData, anonymousEvents: events).getMatchedCriteria()
204-
return matchedCriteriaId
199+
200+
guard let events else { return nil }
201+
202+
return CriteriaCompletionChecker(anonymousCriteria: criteriaData, anonymousEvents: events).getMatchedCriteria()
205203
}
206204

207205
// Gets the anonymous criteria
@@ -212,41 +210,60 @@ public class AnonymousUserManager: AnonymousUserManagerProtocol {
212210
}
213211

214212
// Stores event data locally
215-
private func storeEventData(type: String, data: [AnyHashable: Any], shouldOverWrite: Bool? = false) {
213+
private func storeEventData(type: String, data: [AnyHashable: Any], shouldOverWrite: Bool = false) {
214+
// Early return if no AUT consent was given
216215
if !self.localStorage.anonymousUsageTrack {
217216
ITBInfo("AUT CONSENT NOT GIVEN - no events being stored")
218217
return
219218
}
220-
221-
let storedData = localStorage.anonymousUserEvents
219+
220+
if type == EventType.updateUser {
221+
processAndStoreUserUpdate(data: data, shouldOverWrite: shouldOverWrite)
222+
} else {
223+
processAndStoreEvent(type: type, data: data, shouldOverWrite: shouldOverWrite)
224+
}
225+
226+
if let criteriaId = evaluateCriteriaAndReturnID() {
227+
createKnownUserIfCriteriaMatched(criteriaId)
228+
}
229+
}
230+
231+
// Stores User Update data
232+
private func processAndStoreUserUpdate(data: [AnyHashable: Any], shouldOverWrite: Bool) {
233+
if shouldOverWrite, var userUpdate = localStorage.anonymousUserUpdate {
234+
userUpdate = userUpdate.merging(data) { (_, new) in new }
235+
} else {
236+
var newEventData = data
237+
newEventData.setValue(for: JsonKey.eventType, value: EventType.updateUser)
238+
newEventData.setValue(for: JsonKey.eventTimeStamp, value: IterableUtil.secondsFromEpoch(for: dateProvider.currentDate)) // this we use as unique idenfier too
239+
240+
localStorage.anonymousUserUpdate = newEventData
241+
}
242+
}
243+
244+
// Stores all other event data
245+
private func processAndStoreEvent(type: String, data: [AnyHashable: Any], shouldOverWrite: Bool) {
222246
var eventsDataObjects: [[AnyHashable: Any]] = []
223-
224-
if let _storedData = storedData {
225-
eventsDataObjects = _storedData
247+
248+
if let anonymousUserEvents = localStorage.anonymousUserEvents {
249+
eventsDataObjects.append(contentsOf: anonymousUserEvents)
226250
}
227-
var appendData = data
228-
appendData.setValue(for: JsonKey.eventType, value: type)
229-
appendData.setValue(for: JsonKey.eventTimeStamp, value:IterableUtil.secondsFromEpoch(for: dateProvider.currentDate)) // this we use as unique idenfier too
230-
231-
if shouldOverWrite == true {
232-
let trackingType = type
233-
if let indexToUpdate = eventsDataObjects.firstIndex(where: { $0[JsonKey.eventType] as? String == trackingType }) {
234-
let dataToUpdate = eventsDataObjects[indexToUpdate]
235-
eventsDataObjects[indexToUpdate] = dataToUpdate.merging(data) { (_, new) in new }
236-
} else {
237-
eventsDataObjects.append(appendData)
238-
}
251+
252+
if shouldOverWrite, let indexToUpdate = eventsDataObjects.firstIndex(where: { $0[JsonKey.eventType] as? String == type }) {
253+
let dataToUpdate = eventsDataObjects[indexToUpdate]
254+
eventsDataObjects[indexToUpdate] = dataToUpdate.merging(data) { (_, new) in new }
239255
} else {
240-
eventsDataObjects.append(appendData)
256+
var newEventData = data
257+
newEventData.setValue(for: JsonKey.eventType, value: type)
258+
newEventData.setValue(for: JsonKey.eventTimeStamp, value: IterableUtil.secondsFromEpoch(for: dateProvider.currentDate)) // this we use as unique idenfier too
259+
260+
eventsDataObjects.append(newEventData)
241261
}
242-
243-
let eventDataCount = eventsDataObjects.count
244-
if eventDataCount > config.eventThresholdLimit {
262+
263+
if eventsDataObjects.count > config.eventThresholdLimit {
245264
eventsDataObjects = eventsDataObjects.suffix(config.eventThresholdLimit)
246265
}
266+
247267
localStorage.anonymousUserEvents = eventsDataObjects
248-
if let criteriaId = evaluateCriteriaAndReturnID() {
249-
createKnownUserIfCriteriaMatched(criteriaId)
250-
}
251268
}
252269
}

‎swift-sdk/Internal/AuthManager.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class AuthManager: IterableAuthManagerProtocol {
9393
if localStorage.email != nil || localStorage.userId != nil || localStorage.userIdAnnon != nil {
9494
localStorage.anonymousUserEvents = nil
9595
localStorage.anonymousSessions = nil
96+
localStorage.anonymousUserUpdate = nil
9697
}
9798

9899
isLastAuthTokenValid = false

‎swift-sdk/Internal/InternalIterableAPI.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
232232
self.localStorage.anonymousUsageTrack = isAnonymousUsageTracked
233233
self.localStorage.anonymousUserEvents = nil
234234
self.localStorage.anonymousSessions = nil
235+
self.localStorage.anonymousUserUpdate = nil
236+
235237
if isAnonymousUsageTracked && config.enableAnonTracking {
236238
ITBInfo("CONSENT GIVEN and ANON TRACKING ENABLED - Criteria fetched")
237239
self.anonymousUserManager.getAnonCriteria()

‎swift-sdk/Internal/LocalStorageProtocol.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ protocol LocalStorageProtocol {
2424
var anonymousUsageTrack: Bool { get set }
2525

2626
var anonymousUserEvents: [[AnyHashable: Any]]? { get set }
27+
28+
var anonymousUserUpdate: [AnyHashable: Any]? { get set }
2729

2830
var criteriaData: Data? { get set }
2931

‎tests/common/MockLocalStorage.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class MockLocalStorage: LocalStorageProtocol {
3131
var offlineMode: Bool = false
3232

3333
var anonymousUsageTrack: Bool = true
34+
35+
var anonymousUserUpdate: [AnyHashable : Any]?
3436

3537
func getAttributionInfo(currentDate: Date) -> IterableAttributionInfo? {
3638
guard !MockLocalStorage.isExpired(expiration: attributionInfoExpiration, currentDate: currentDate) else {

0 commit comments

Comments
 (0)