@@ -31,7 +31,7 @@ import OneSignalOSCore
31
31
32
32
/**
33
33
Involved in the login process and responsible for Identify User and Create User.
34
- Can execute `OSRequestCreateUser`, `OSRequestIdentifyUser`, `OSRequestTransferSubscription`, `OSRequestFetchUser`.
34
+ Can execute `OSRequestCreateUser`, `OSRequestIdentifyUser`, `OSRequestTransferSubscription`, `OSRequestFetchUser`, `OSRequestFetchIdentityBySubscription` .
35
35
*/
36
36
class OSUserExecutor {
37
37
static var userRequestQueue : [ OSUserRequest ] = [ ]
@@ -42,11 +42,24 @@ class OSUserExecutor {
42
42
static func start( ) {
43
43
var userRequestQueue : [ OSUserRequest ] = [ ]
44
44
45
- // Read unfinished Create User + Identify User requests from cache, if any...
45
+ // Read unfinished Create User + Identify User + Get Identity By Subscription requests from cache, if any...
46
46
if let cachedRequestQueue = OneSignalUserDefaults . initShared ( ) . getSavedCodeableData ( forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, defaultValue: [ ] ) as? [ OSUserRequest ] {
47
47
// Hook each uncached Request to the right model reference
48
48
for request in cachedRequestQueue {
49
- if request. isKind ( of: OSRequestCreateUser . self) , let req = request as? OSRequestCreateUser {
49
+ if request. isKind ( of: OSRequestFetchIdentityBySubscription . self) , let req = request as? OSRequestFetchIdentityBySubscription {
50
+ if let identityModel = OneSignalUserManagerImpl . sharedInstance. identityModelStore. getModel ( modelId: req. identityModel. modelId) {
51
+ // 1. The model exist in the store, set it to be the Request's model
52
+ req. identityModel = identityModel
53
+ } else if let identityModel = identityModels [ req. identityModel. modelId] {
54
+ // 2. The model exists in the dict of identityModels already processed to use
55
+ req. identityModel = identityModel
56
+ } else {
57
+ // 3. The models do not exist, use the model on the request, and add to dict.
58
+ identityModels [ req. identityModel. modelId] = req. identityModel
59
+ }
60
+ userRequestQueue. append ( req)
61
+
62
+ } else if request. isKind ( of: OSRequestCreateUser . self) , let req = request as? OSRequestCreateUser {
50
63
if let identityModel = OneSignalUserManagerImpl . sharedInstance. identityModelStore. getModel ( modelId: req. identityModel. modelId) {
51
64
// 1. The model exist in the store, set it to be the Request's model
52
65
req. identityModel = identityModel
@@ -144,8 +157,11 @@ class OSUserExecutor {
144
157
if !request. prepareForExecution ( ) {
145
158
return
146
159
}
147
-
148
- if request. isKind ( of: OSRequestCreateUser . self) , let createUserRequest = request as? OSRequestCreateUser {
160
+
161
+ if request. isKind ( of: OSRequestFetchIdentityBySubscription . self) , let fetchIdentityRequest = request as? OSRequestFetchIdentityBySubscription {
162
+ executeFetchIdentityBySubscriptionRequest ( fetchIdentityRequest)
163
+ return
164
+ } else if request. isKind ( of: OSRequestCreateUser . self) , let createUserRequest = request as? OSRequestCreateUser {
149
165
executeCreateUserRequest ( createUserRequest)
150
166
return
151
167
} else if request. isKind ( of: OSRequestIdentifyUser . self) , let identifyUserRequest = request as? OSRequestIdentifyUser {
@@ -262,8 +278,8 @@ class OSUserExecutor {
262
278
return response ? [ " properties " ] as? [ String : Any ]
263
279
}
264
280
265
- static func parseIdentityObjectResponse( _ response: [ AnyHashable : Any ] ? ) -> [ String : Any ] ? {
266
- return response ? [ " identity " ] as? [ String : Any ]
281
+ static func parseIdentityObjectResponse( _ response: [ AnyHashable : Any ] ? ) -> [ String : String ] ? {
282
+ return response ? [ " identity " ] as? [ String : String ]
267
283
}
268
284
269
285
// We will pass minimal properties to this request
@@ -304,7 +320,7 @@ class OSUserExecutor {
304
320
// If this user already exists and we logged into an external_id, fetch the user data
305
321
// TODO: Only do this if response code is 200 or 202
306
322
// Fetch the user only if its the current user
307
- if let _ = OneSignalUserManagerImpl . sharedInstance. identityModelStore . getModel ( modelId : request. identityModel. modelId ) ,
323
+ if OneSignalUserManagerImpl . sharedInstance. isCurrentUser ( request. identityModel) ,
308
324
let identity = request. parameters ? [ " identity " ] as? [ String : String ] ,
309
325
let externalId = identity [ OS_EXTERNAL_ID] {
310
326
fetchUser ( aliasLabel: OS_EXTERNAL_ID, aliasId: externalId, identityModel: request. identityModel)
@@ -323,7 +339,60 @@ class OSUserExecutor {
323
339
executePendingRequests ( )
324
340
}
325
341
}
342
+
343
+ static func fetchIdentityBySubscription( _ user: OSUserInternal ) {
344
+ let request = OSRequestFetchIdentityBySubscription ( identityModel: user. identityModel, pushSubscriptionModel: user. pushSubscriptionModel)
345
+
346
+ appendToQueue ( request)
347
+ executePendingRequests ( )
348
+ }
349
+
350
+ /**
351
+ For migrating legacy players from 3.x to 5.x. This request will fetch the identity object for a subscription ID, and we will use the returned onesignalId to fetch and hydrate the local user.
352
+ */
353
+ static func executeFetchIdentityBySubscriptionRequest( _ request: OSRequestFetchIdentityBySubscription ) {
354
+ guard !request. sentToClient else {
355
+ return
356
+ }
357
+ guard request. prepareForExecution ( ) else {
358
+ return
359
+ }
360
+ request. sentToClient = true
326
361
362
+ OneSignalClient . shared ( ) . execute ( request) { response in
363
+ removeFromQueue ( request)
364
+
365
+ if let identityObject = parseIdentityObjectResponse ( response) ,
366
+ let onesignalId = identityObject [ OS_ONESIGNAL_ID]
367
+ {
368
+ request. identityModel. hydrate ( identityObject)
369
+
370
+ // Fetch this user's data if it is the current user
371
+ guard OneSignalUserManagerImpl . sharedInstance. isCurrentUser ( request. identityModel)
372
+ else {
373
+ executePendingRequests ( )
374
+ return
375
+ }
376
+
377
+ fetchUser ( aliasLabel: OS_ONESIGNAL_ID, aliasId: onesignalId, identityModel: request. identityModel)
378
+ }
379
+ } onFailure: { error in
380
+ OneSignalLog . onesignalLog ( . LL_ERROR, message: " OSUserExecutor executeFetchIdentityBySubscriptionRequest failed with error: \( error. debugDescription) " )
381
+
382
+ // TODO: Differentiate error cases
383
+
384
+ // If the error is not retryable, remove from cache and queue
385
+ if let nsError = error as? NSError ,
386
+ nsError. code < 500 && nsError. code != 0 {
387
+ // remove the subscription_id but keep the same push subscription model
388
+ OneSignalUserManagerImpl . sharedInstance. pushSubscriptionModelStore. getModels ( ) [ OS_PUSH_SUBSCRIPTION_MODEL_KEY] ? . subscriptionId = nil
389
+ removeFromQueue ( request)
390
+ }
391
+ // Otherwise it is a retryable error
392
+ executePendingRequests ( )
393
+ }
394
+ }
395
+
327
396
static func identifyUser( externalId: String , identityModelToIdentify: OSIdentityModel , identityModelToUpdate: OSIdentityModel ) {
328
397
let request = OSRequestIdentifyUser (
329
398
aliasLabel: OS_EXTERNAL_ID,
@@ -352,22 +421,21 @@ class OSUserExecutor {
352
421
353
422
// the anonymous user has been identified, still need to Fetch User as we cleared local data
354
423
// Fetch the user only if its the current user
355
- if let _ = OneSignalUserManagerImpl . sharedInstance. identityModelStore . getModel ( modelId : request. identityModelToUpdate. modelId ) {
424
+ if OneSignalUserManagerImpl . sharedInstance. isCurrentUser ( request. identityModelToUpdate) {
356
425
fetchUser ( aliasLabel: OS_EXTERNAL_ID, aliasId: request. aliasId, identityModel: request. identityModelToUpdate)
357
426
} else {
358
427
executePendingRequests ( )
359
428
}
360
429
} onFailure: { error in
361
430
OneSignalLog . onesignalLog ( . LL_VERBOSE, message: " executeIdentifyUserRequest failed with error \( error. debugDescription) " )
362
- removeFromQueue ( request)
363
431
// Returns 409 if any provided (label, id) pair exists on another User, so the SDK will switch to this user.
364
432
if let nsError = error as? NSError {
365
433
if nsError. code == 409 {
366
434
OneSignalLog . onesignalLog ( . LL_VERBOSE, message: " executeIdentifyUserRequest returned 409, failed due to alias already assigned to a different user. Now switch to this user. " )
367
435
368
436
removeFromQueue ( request)
369
437
// Fetch the user only if its the current user
370
- if let _ = OneSignalUserManagerImpl . sharedInstance. identityModelStore . getModel ( modelId : request. identityModelToUpdate. modelId ) {
438
+ if OneSignalUserManagerImpl . sharedInstance. isCurrentUser ( request. identityModelToUpdate) {
371
439
fetchUser ( aliasLabel: OS_EXTERNAL_ID, aliasId: request. aliasId, identityModel: request. identityModelToUpdate)
372
440
// TODO: Link ^ to the new user... what was this todo for?
373
441
}
@@ -581,6 +649,69 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest {
581
649
}
582
650
}
583
651
652
+ class OSRequestFetchIdentityBySubscription : OneSignalRequest , OSUserRequest {
653
+ var sentToClient = false
654
+ let stringDescription : String
655
+
656
+ override var description : String {
657
+ return stringDescription
658
+ }
659
+
660
+ var identityModel : OSIdentityModel
661
+ var pushSubscriptionModel : OSSubscriptionModel
662
+
663
+ func prepareForExecution( ) -> Bool {
664
+ guard let appId = OneSignalConfigManager . getAppId ( ) else {
665
+ OneSignalLog . onesignalLog ( . LL_DEBUG, message: " Cannot generate the FetchIdentityBySubscription request due to null app ID. " )
666
+ return false
667
+ }
668
+
669
+ if let subscriptionId = pushSubscriptionModel. subscriptionId {
670
+ self . path = " apps/ \( appId) /subscriptions/ \( subscriptionId) /user/identity "
671
+ return true
672
+ } else {
673
+ // This is an error, and should never happen
674
+ OneSignalLog . onesignalLog ( . LL_ERROR, message: " Cannot generate the FetchIdentityBySubscription request due to null subscriptionId. " )
675
+ self . path = " "
676
+ return false
677
+ }
678
+ }
679
+
680
+ init ( identityModel: OSIdentityModel , pushSubscriptionModel: OSSubscriptionModel ) {
681
+ self . identityModel = identityModel
682
+ self . pushSubscriptionModel = pushSubscriptionModel
683
+ self . stringDescription = " OSRequestFetchIdentityBySubscription with subscriptionId: \( pushSubscriptionModel. subscriptionId ?? " nil " ) "
684
+ super. init ( )
685
+ self . method = GET
686
+ }
687
+
688
+ func encode( with coder: NSCoder ) {
689
+ coder. encode ( identityModel, forKey: " identityModel " )
690
+ coder. encode ( pushSubscriptionModel, forKey: " pushSubscriptionModel " )
691
+ coder. encode ( method. rawValue, forKey: " method " ) // Encodes as String
692
+ coder. encode ( timestamp, forKey: " timestamp " )
693
+ }
694
+
695
+ required init ? ( coder: NSCoder ) {
696
+ guard
697
+ let identityModel = coder. decodeObject ( forKey: " identityModel " ) as? OSIdentityModel ,
698
+ let pushSubscriptionModel = coder. decodeObject ( forKey: " pushSubscriptionModel " ) as? OSSubscriptionModel ,
699
+ let rawMethod = coder. decodeObject ( forKey: " method " ) as? UInt32 ,
700
+ let timestamp = coder. decodeObject ( forKey: " timestamp " ) as? Date
701
+ else {
702
+ // Log error
703
+ return nil
704
+ }
705
+ self . identityModel = identityModel
706
+ self . pushSubscriptionModel = pushSubscriptionModel
707
+
708
+ self . stringDescription = " OSRequestFetchIdentityBySubscription with subscriptionId: \( pushSubscriptionModel. subscriptionId ?? " nil " ) "
709
+ super. init ( )
710
+ self . method = HTTPMethod ( rawValue: rawMethod)
711
+ self . timestamp = timestamp
712
+ }
713
+ }
714
+
584
715
/**
585
716
The `identityModelToIdentify` is used for the `onesignal_id` of the user we want to associate with this alias.
586
717
This request will tell us if we should continue with the previous user who is now identitfied, or to change users to the one this alias already exists on.
0 commit comments