@@ -8,7 +8,69 @@ public class Amplitude {
88 sessions. sessionId
99 }
1010
11- var state : State = State ( )
11+ private let identityLock = NSLock ( )
12+ private var _identity = Identity ( )
13+ public var identity : Identity {
14+ get {
15+ identityLock. withLock {
16+ return _identity
17+ }
18+ }
19+ set {
20+ applyIdentityUpdate ( newValue)
21+ }
22+ }
23+
24+ private func applyIdentityUpdate( _ identity: Identity , sendIdentifyIfNeeded: Bool = true ) {
25+ var deviceIdChanged = false
26+ var userIdChanged = false
27+ var userPropertiesChanged = false
28+ identityLock. withLock {
29+ let oldValue = _identity
30+ _identity = identity
31+
32+ if identity. deviceId != oldValue. deviceId {
33+ deviceIdChanged = true
34+ try ? storage. write ( key: . DEVICE_ID, value: identity. deviceId)
35+ }
36+
37+ if identity. userId != oldValue. userId {
38+ userIdChanged = true
39+ try ? storage. write ( key: . USER_ID, value: identity. userId)
40+ }
41+
42+ // Convert to NSDictionary to allow comparison
43+ let oldUserProperties = oldValue. userProperties as NSDictionary
44+ let newUserProperties = identity. userProperties as NSDictionary
45+ if oldUserProperties != newUserProperties {
46+ userPropertiesChanged = true
47+ }
48+ }
49+
50+ // Inform plugins after we've relinquished the lock
51+ if userIdChanged {
52+ timeline. apply { plugin in
53+ plugin. onUserIdChanged ( identity. userId)
54+ }
55+ }
56+
57+ if deviceIdChanged {
58+ timeline. apply { plugin in
59+ plugin. onDeviceIdChanged ( identity. deviceId)
60+ }
61+ }
62+
63+ if userIdChanged || deviceIdChanged || userPropertiesChanged {
64+ timeline. apply { plugin in
65+ plugin. onIdentityChanged ( identity)
66+ }
67+ }
68+
69+ if sendIdentifyIfNeeded, userPropertiesChanged {
70+ identify ( userProperties: identity. userProperties)
71+ }
72+ }
73+
1274 var contextPlugin : ContextPlugin
1375 let timeline = Timeline ( )
1476
@@ -56,12 +118,9 @@ public class Amplitude {
56118 }
57119 migrateInstanceOnlyStorages ( )
58120
59- if let deviceId: String ? = configuration. storageProvider. read ( key: . DEVICE_ID) {
60- state. deviceId = deviceId
61- }
62- if let userId: String ? = configuration. storageProvider. read ( key: . USER_ID) {
63- state. userId = userId
64- }
121+ _identity = Identity ( userId: configuration. storageProvider. read ( key: . USER_ID) ,
122+ deviceId: configuration. storageProvider. read ( key: . DEVICE_ID) ,
123+ userProperties: [ : ] )
65124
66125 if configuration. offline != NetworkConnectivityCheckerPlugin . Disabled,
67126 VendorSystem . current. networkConnectivityCheckingEnabled {
@@ -78,7 +137,9 @@ public class Amplitude {
78137
79138 // Monitor changes to optOut to send to Timeline
80139 configuration. optOutChanged = { [ weak self] optOut in
81- self ? . timeline. onOptOutChanged ( optOut)
140+ self ? . timeline. apply {
141+ $0. onOptOutChanged ( optOut)
142+ }
82143 }
83144
84145 trackingQueue. async { [ self ] in
@@ -131,11 +192,19 @@ public class Amplitude {
131192 event. userProperties = identify. properties as [ String : Any ]
132193 if let eventOptions = options {
133194 event. mergeEventOptions ( eventOptions: eventOptions)
134- if eventOptions. userId != nil {
135- setUserId ( userId: eventOptions. userId)
195+
196+ var identity = self . identity
197+ var identityChanged = false
198+ if let userId = eventOptions. userId {
199+ identityChanged = true
200+ identity. userId = userId
201+ }
202+ if let deviceId = eventOptions. deviceId {
203+ identityChanged = true
204+ identity. deviceId = deviceId
136205 }
137- if eventOptions . deviceId != nil {
138- setDeviceId ( deviceId : eventOptions . deviceId )
206+ if identityChanged {
207+ self . identity = identity
139208 }
140209 }
141210 process ( event: event)
@@ -245,21 +314,13 @@ public class Amplitude {
245314 @discardableResult
246315 public func add( plugin: Plugin ) -> Amplitude {
247316 plugin. setup ( amplitude: self )
248- if let _plugin = plugin as? ObservePlugin {
249- state. add ( plugin: _plugin)
250- } else {
251- timeline. add ( plugin: plugin)
252- }
317+ timeline. add ( plugin: plugin)
253318 return self
254319 }
255320
256321 @discardableResult
257322 public func remove( plugin: Plugin ) -> Amplitude {
258- if let _plugin = plugin as? ObservePlugin {
259- state. remove ( plugin: _plugin)
260- } else {
261- timeline. remove ( plugin: plugin)
262- }
323+ timeline. remove ( plugin: plugin)
263324 return self
264325 }
265326
@@ -277,26 +338,22 @@ public class Amplitude {
277338
278339 @discardableResult
279340 public func setUserId( userId: String ? ) -> Amplitude {
280- try ? storage. write ( key: . USER_ID, value: userId)
281- state. userId = userId
282- timeline. onUserIdChanged ( userId)
341+ identity. userId = userId
283342 return self
284343 }
285344
286345 @discardableResult
287346 public func setDeviceId( deviceId: String ? ) -> Amplitude {
288- try ? storage. write ( key: . DEVICE_ID, value: deviceId)
289- state. deviceId = deviceId
290- timeline. onDeviceIdChanged ( deviceId)
347+ identity. deviceId = deviceId
291348 return self
292349 }
293350
294351 public func getUserId( ) -> String ? {
295- return state . userId
352+ return identity . userId
296353 }
297354
298355 public func getDeviceId( ) -> String ? {
299- return state . deviceId
356+ return identity . deviceId
300357 }
301358
302359 public func getSessionId( ) -> Int64 {
@@ -329,6 +386,7 @@ public class Amplitude {
329386 @discardableResult
330387 public func reset( ) -> Amplitude {
331388 setUserId ( userId: nil )
389+ identity. userProperties. removeAll ( )
332390 contextPlugin. initializeDeviceId ( forceReset: true )
333391 return self
334392 }
@@ -342,6 +400,17 @@ public class Amplitude {
342400 logger? . log ( message: " Skip event based on opt out configuration " )
343401 return
344402 }
403+
404+ if event. eventType == Constants . IDENTIFY_EVENT, let userProperties = event. userProperties {
405+ var updatedIdentity = identity
406+ updatedIdentity. apply ( identify: userProperties as [ String : Any ] )
407+ applyIdentityUpdate ( updatedIdentity, sendIdentifyIfNeeded: false )
408+ }
409+
410+ let identity = self . identity
411+ event. userId = event. userId ?? identity. userId
412+ event. deviceId = event. deviceId ?? identity. deviceId
413+
345414 let inForeground = inForeground
346415 trackingQueue. async { [ self ] in
347416 let events = self . sessions. processEvent ( event: event, inForeground: inForeground)
0 commit comments