Skip to content

Commit 0e63d08

Browse files
committed
✨Added consent logging
1 parent eefbf17 commit 0e63d08

File tree

9 files changed

+120
-1
lines changed

9 files changed

+120
-1
lines changed

swift-sdk/Core/Constants.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ enum Const {
5454
static let mergeUser = "users/merge";
5555
static let getCriteria = "anonymoususer/list";
5656
static let trackAnonSession = "anonymoususer/events/session";
57+
static let trackConsent = "anonymoususer/events/trackConsent";
5758
static let getEmbeddedMessages = "embedded-messaging/messages"
5859
static let embeddedMessageReceived = "embedded-messaging/events/received"
5960
static let embeddedMessageClick = "embedded-messaging/events/click"
@@ -78,6 +79,7 @@ enum Const {
7879
static let matchedCriteria = "itbl_matched_criteria"
7980
static let eventList = "itbl_event_list"
8081
static let anonymousUsageTrack = "itbl_anonymous_usage_track"
82+
static let visitorConsentTimestamp = "itbl_visitor_consent_timestamp"
8183
static let isNotificationsEnabled = "itbl_isNotificationsEnabled"
8284
static let hasStoredNotificationSetting = "itbl_hasStoredNotificationSetting"
8385

@@ -265,7 +267,11 @@ enum JsonKey {
265267

266268
static let frameworkType = "frameworkType"
267269

268-
// embedded
270+
// Consent tracking
271+
static let consentTimestamp = "consentTimestamp"
272+
static let isUserKnown = "isUserKnown"
273+
274+
// Embedded Messages
269275
static let embeddedSessionId = "session"
270276
static let placementId = "placementId"
271277
static let embeddedSessionStart = "embeddedSessionStart"

swift-sdk/Internal/AnonymousUserManager.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ public class AnonymousUserManager: AnonymousUserManagerProtocol {
193193

194194
IterableAPI.implementation?.setUserId(userId, isAnon: true)
195195

196+
// Send consent data after session creation
197+
self.sendConsentAfterCriteriaMatch(userId: userId)
198+
196199
self.syncNonSyncedEvents()
197200
}
198201
}
@@ -266,4 +269,23 @@ public class AnonymousUserManager: AnonymousUserManagerProtocol {
266269

267270
localStorage.anonymousUserEvents = eventsDataObjects
268271
}
272+
273+
/// Sends consent data after user meets criteria and anonymous user is created
274+
private func sendConsentAfterCriteriaMatch(userId: String) {
275+
guard let consentTimestamp = localStorage.visitorConsentTimestamp else {
276+
ITBInfo("No consent timestamp found, skipping consent tracking")
277+
return
278+
}
279+
280+
IterableAPI.implementation?.apiClient.trackConsent(
281+
consentTimestamp: consentTimestamp,
282+
email: nil,
283+
userId: userId,
284+
isUserKnown: false
285+
).onSuccess { _ in
286+
ITBInfo("Consent tracked successfully for criteria match")
287+
}.onError { error in
288+
ITBError("Failed to track consent for criteria match: \(error)")
289+
}
290+
}
269291
}

swift-sdk/Internal/InternalIterableAPI.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
161161
isEmail: true,
162162
failureHandler: failureHandler
163163
)
164+
// Send consent for replay scenario if user didn't meet criteria but had consent
165+
self?.sendConsentForReplayScenario(email: email, userId: nil)
166+
164167
self?.localStorage.userIdAnnon = nil
165168
}
166169
}
@@ -203,6 +206,8 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
203206
isEmail: false,
204207
failureHandler: failureHandler
205208
)
209+
// Send consent for replay scenario if user didn't meet criteria but had consent
210+
self?.sendConsentForReplayScenario(email: nil, userId: userId)
206211
}
207212

208213
if !isAnon {
@@ -238,6 +243,14 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
238243
func setVisitorUsageTracked(isVisitorUsageTracked: Bool) {
239244
ITBInfo("CONSENT CHANGED - local events cleared")
240245
self.localStorage.anonymousUsageTrack = isVisitorUsageTracked
246+
247+
// Store consent timestamp when consent is given
248+
if isVisitorUsageTracked {
249+
self.localStorage.visitorConsentTimestamp = Int64(dateProvider.currentDate.timeIntervalSince1970)
250+
} else {
251+
self.localStorage.visitorConsentTimestamp = nil
252+
}
253+
241254
self.localStorage.anonymousUserEvents = nil
242255
self.localStorage.anonymousSessions = nil
243256
self.localStorage.anonymousUserUpdate = nil
@@ -254,6 +267,29 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
254267
return self.localStorage.anonymousUsageTrack
255268
}
256269

270+
/// Sends consent data for replay scenarios when user signs up/in but didn't meet criteria
271+
private func sendConsentForReplayScenario(email: String?, userId: String?) {
272+
guard let consentTimestamp = localStorage.visitorConsentTimestamp else {
273+
return
274+
}
275+
276+
// Only send consent if we have previous anonymous tracking consent but no anonymous user ID
277+
guard localStorage.userIdAnnon == nil && localStorage.anonymousUsageTrack else {
278+
return
279+
}
280+
281+
apiClient.trackConsent(
282+
consentTimestamp: consentTimestamp,
283+
email: email,
284+
userId: userId,
285+
isUserKnown: true
286+
).onSuccess { _ in
287+
ITBInfo("Consent tracked successfully for replay scenario")
288+
}.onError { error in
289+
ITBError("Failed to track consent for replay scenario: \(error)")
290+
}
291+
}
292+
257293
// MARK: - API Request Calls
258294

259295
func register(token: String,

swift-sdk/Internal/IterableUserDefaults.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ class IterableUserDefaults {
7878
}
7979
}
8080

81+
var visitorConsentTimestamp: Int64? {
82+
get {
83+
return int64(withKey: .visitorConsentTimestamp)
84+
} set {
85+
save(int64: newValue, withKey: .visitorConsentTimestamp)
86+
}
87+
}
88+
8189
var anonymousUserEvents: [[AnyHashable: Any]]? {
8290
get {
8391
return eventData(withKey: .anonymousUserEvents)
@@ -242,6 +250,14 @@ class IterableUserDefaults {
242250
userDefaults.object(forKey: key.value) as? Data
243251
}
244252

253+
private func int64(withKey key: UserDefaultsKey) -> Int64? {
254+
userDefaults.object(forKey: key.value) as? Int64
255+
}
256+
257+
private func save(int64: Int64?, withKey key: UserDefaultsKey) {
258+
userDefaults.set(int64, forKey: key.value)
259+
}
260+
245261
private static func isExpired(expiration: Date?, currentDate: Date) -> Bool {
246262
if let expiration = expiration {
247263
if expiration.timeIntervalSinceReferenceDate > currentDate.timeIntervalSinceReferenceDate {
@@ -326,6 +342,7 @@ class IterableUserDefaults {
326342
static let criteriaData = UserDefaultsKey(value: Const.UserDefault.criteriaData)
327343
static let anonymousSessions = UserDefaultsKey(value: Const.UserDefault.anonymousSessions)
328344
static let anonymousUsageTrack = UserDefaultsKey(value: Const.UserDefault.anonymousUsageTrack)
345+
static let visitorConsentTimestamp = UserDefaultsKey(value: Const.UserDefault.visitorConsentTimestamp)
329346

330347
static let isNotificationsEnabled = UserDefaultsKey(value: Const.UserDefault.isNotificationsEnabled)
331348
static let hasStoredNotificationSetting = UserDefaultsKey(value: Const.UserDefault.hasStoredNotificationSetting)

swift-sdk/Internal/Utilities/LocalStorage.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ struct LocalStorage: LocalStorageProtocol {
116116
}
117117
}
118118

119+
var visitorConsentTimestamp: Int64? {
120+
get {
121+
iterableUserDefaults.visitorConsentTimestamp
122+
} set {
123+
iterableUserDefaults.visitorConsentTimestamp = newValue
124+
}
125+
}
126+
119127
var isNotificationsEnabled: Bool {
120128
get {
121129
iterableUserDefaults.isNotificationsEnabled

swift-sdk/Internal/Utilities/LocalStorageProtocol.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ protocol LocalStorageProtocol {
2323

2424
var anonymousUsageTrack: Bool { get set }
2525

26+
var visitorConsentTimestamp: Int64? { get set }
27+
2628
var anonymousUserEvents: [[AnyHashable: Any]]? { get set }
2729

2830
var anonymousUserUpdate: [AnyHashable: Any]? { get set }

swift-sdk/Internal/api-client/ApiClient.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,12 @@ extension ApiClient: ApiClientProtocol {
292292
let result = createRequestCreator().flatMap { $0.createTrackAnonSessionRequest(createdAt: createdAt, withUserId: userId, dataFields: dataFields, requestJson: requestJson) }
293293
return send(iterableRequestResult: result)
294294
}
295+
296+
func trackConsent(consentTimestamp: Int64, email: String?, userId: String?, isUserKnown: Bool) -> Pending<SendRequestValue, SendRequestError> {
297+
let result = createRequestCreator().flatMap { $0.createTrackConsentRequest(consentTimestamp: consentTimestamp, email: email, userId: userId, isUserKnown: isUserKnown) }
298+
return send(iterableRequestResult: result)
299+
}
300+
295301
// MARK: - Embedded Messaging
296302

297303
func getEmbeddedMessages() -> Pending<PlacementsPayload, SendRequestError> {

swift-sdk/Internal/api-client/ApiClientProtocol.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ protocol ApiClientProtocol: AnyObject {
5757
func getCriteria() -> Pending<SendRequestValue, SendRequestError>
5858

5959
func trackAnonSession(createdAt: Int, withUserId userId: String, dataFields: [AnyHashable: Any]?, requestJson: [AnyHashable: Any]) -> Pending<SendRequestValue, SendRequestError>
60+
61+
func trackConsent(consentTimestamp: Int64, email: String?, userId: String?, isUserKnown: Bool) -> Pending<SendRequestValue, SendRequestError>
62+
6063
func getEmbeddedMessages() -> Pending<PlacementsPayload, SendRequestError>
6164

6265
@discardableResult func track(embeddedMessageReceived message: IterableEmbeddedMessage) -> Pending<SendRequestValue, SendRequestError>

swift-sdk/Internal/api-client/Request/RequestCreator.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,25 @@ struct RequestCreator {
702702
return .success(.post(createPostRequest(path: Const.Path.trackAnonSession, body: body)))
703703
}
704704

705+
func createTrackConsentRequest(consentTimestamp: Int64, email: String?, userId: String?, isUserKnown: Bool) -> Result<IterableRequest, IterableError> {
706+
var body = [AnyHashable: Any]()
707+
708+
body.setValue(for: JsonKey.consentTimestamp, value: Int(consentTimestamp))
709+
710+
if let email = email {
711+
body.setValue(for: JsonKey.email, value: email)
712+
}
713+
714+
if let userId = userId {
715+
body.setValue(for: JsonKey.userId, value: userId)
716+
}
717+
718+
body.setValue(for: JsonKey.isUserKnown, value: isUserKnown)
719+
body.setValue(for: JsonKey.deviceInfo, value: deviceMetadata.asDictionary())
720+
721+
return .success(.post(createPostRequest(path: Const.Path.trackConsent, body: body)))
722+
}
723+
705724
// MARK: - PRIVATE
706725

707726
private static let authMissingMessage = "Both email and userId are nil"

0 commit comments

Comments
 (0)