1
1
/* *
2
2
* Modified MIT License
3
3
*
4
- * Copyright 2017 OneSignal
4
+ * Copyright 2024 OneSignal
5
5
*
6
6
* Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
* of this software and associated documentation files (the "Software"), to deal
35
35
#import " OSInAppMessagePrompt.h"
36
36
#import " OSInAppMessagingRequests.h"
37
37
#import " OneSignalWebViewManager.h"
38
+ #import " OneSignalTracker.h"
38
39
#import < OneSignalOutcomes/OneSignalOutcomes.h>
39
40
#import " OSSessionManager.h"
41
+ #import " OneSignalOSCore/OneSignalOSCore-Swift.h"
42
+
43
+ static NSInteger const DEFAULT_RETRY_AFTER_SECONDS = 1 ; // Default 1 second retry delay
44
+ static NSInteger const DEFAULT_RETRY_LIMIT = 0 ; // If not returned by backend, don't retry
45
+ static NSInteger const IAM_FETCH_DELAY_BUFFER = 0.5 ; // Fallback value if ryw_delay is nil: delay by 500 ms to increase the probability of getting a 200 & not having to retry
40
46
41
47
@implementation OSInAppMessageWillDisplayEvent
42
48
@@ -242,22 +248,70 @@ - (void)updateInAppMessagesFromCache {
242
248
}
243
249
244
250
- (void )getInAppMessagesFromServer : (NSString *)subscriptionId {
245
- [OneSignalLog onesignalLog: ONE_S_LL_VERBOSE message: @" getInAppMessagesFromServer" ];
251
+ dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
252
+ [OneSignalLog onesignalLog: ONE_S_LL_VERBOSE message: @" getInAppMessagesFromServer" ];
246
253
247
- if (!subscriptionId) {
248
- [self updateInAppMessagesFromCache ];
249
- return ;
250
- }
251
-
252
- OSRequestGetInAppMessages *request = [OSRequestGetInAppMessages withSubscriptionId: subscriptionId];
253
- [OneSignalCoreImpl.sharedClient executeRequest: request onSuccess: ^(NSDictionary *result) {
254
+ if (!subscriptionId) {
255
+ [self updateInAppMessagesFromCache ];
256
+ return ;
257
+ }
258
+
259
+ OSConsistencyManager *consistencyManager = [OSConsistencyManager shared ];
260
+ NSString *onesignalId = OneSignalUserManagerImpl.sharedInstance .onesignalId ;
261
+
262
+ if (!onesignalId) {
263
+ [OneSignalLog onesignalLog: ONE_S_LL_VERBOSE message: @" Failed to get in app messages due to no OneSignal ID" ];
264
+ return ;
265
+ }
266
+
267
+ OSIamFetchReadyCondition *condition = [OSIamFetchReadyCondition sharedInstanceWithId: onesignalId];
268
+ OSReadYourWriteData *rywData = [consistencyManager getRywTokenFromAwaitableCondition: condition forId: onesignalId];
269
+
270
+ // We need to delay the first request by however long the backend is telling us (`ryw_delay`)
271
+ // This will help avoid unnecessary retries & can be easily adjusted from the backend
272
+ NSTimeInterval rywDelayInSeconds;
273
+ if (rywData.rywDelay ) {
274
+ rywDelayInSeconds = [rywData.rywDelay doubleValue ] / 1000.0 ;
275
+ } else {
276
+ rywDelayInSeconds = IAM_FETCH_DELAY_BUFFER;
277
+ }
278
+ dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t )(rywDelayInSeconds * NSEC_PER_SEC)), dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
279
+
280
+ // Initial request
281
+ [self attemptFetchWithRetries: subscriptionId
282
+ rywData: rywData
283
+ attempts: @0 // Starting with 0 attempts
284
+ retryLimit: nil ]; // Retry limit to be set dynamically on first failure
285
+ });
286
+ });
287
+ }
288
+
289
+
290
+ - (void )attemptFetchWithRetries : (NSString *)subscriptionId
291
+ rywData : (OSReadYourWriteData *)rywData
292
+ attempts : (NSNumber *)attempts
293
+ retryLimit : (NSNumber *)retryLimit {
294
+ NSNumber *sessionDuration = @([OSSessionManager.sharedSessionManager getTimeFocusedElapsed ]);
295
+ NSString *rywToken = rywData.rywToken ;
296
+ NSNumber *rywDelay = rywData.rywDelay ;
297
+
298
+ // Create the request with the current attempt count
299
+ OSRequestGetInAppMessages *request = [OSRequestGetInAppMessages withSubscriptionId: subscriptionId
300
+ withSessionDuration: sessionDuration
301
+ withRetryCount: attempts
302
+ withRywToken: rywToken];
303
+
304
+ __block NSNumber *blockRetryLimit = retryLimit;
305
+
306
+ [OneSignalCoreImpl.sharedClient executeRequest: request
307
+ onSuccess: ^(NSDictionary *result) {
254
308
dispatch_async (dispatch_get_main_queue (), ^{
255
309
[OneSignalLog onesignalLog: ONE_S_LL_VERBOSE message: @" getInAppMessagesFromServer success" ];
256
- if (result[@" in_app_messages" ]) { // when there are no IAMs, will this still be there?
257
- let messages = [NSMutableArray new ];
310
+ if (result[@" in_app_messages" ]) {
311
+ NSMutableArray * messages = [NSMutableArray new ];
258
312
259
313
for (NSDictionary *messageJson in result[@" in_app_messages" ]) {
260
- let message = [OSInAppMessageInternal instanceWithJson: messageJson];
314
+ OSInAppMessageInternal * message = [OSInAppMessageInternal instanceWithJson: messageJson];
261
315
if (message) {
262
316
[messages addObject: message];
263
317
}
@@ -266,11 +320,89 @@ - (void)getInAppMessagesFromServer:(NSString *)subscriptionId {
266
320
[self updateInAppMessagesFromServer: messages];
267
321
return ;
268
322
}
323
+ });
324
+ }
325
+ onFailure: ^(NSError *error) {
326
+ NSDictionary *errorInfo = error.userInfo [@" returned" ];
327
+ NSNumber *statusCode = errorInfo[@" httpStatusCode" ];
328
+ NSDictionary * responseHeaders = errorInfo[@" headers" ];
329
+
330
+ if (!statusCode) {
331
+ [self updateInAppMessagesFromCache ];
332
+ return ;
333
+ }
334
+
335
+ [OneSignalLog onesignalLog: ONE_S_LL_VERBOSE message: [NSString stringWithFormat: @" getInAppMessagesFromServer failure: %@ " , error.localizedDescription]];
336
+
337
+ NSInteger code = [statusCode integerValue ];
338
+ if (code == 425 || code == 429 ) { // 425 Too Early or 429 Too Many Requests
339
+ NSInteger retryAfter = [responseHeaders[@" Retry-After" ] integerValue ] ?: DEFAULT_RETRY_AFTER_SECONDS;
269
340
270
- // TODO: Check this request and response. If no IAMs returned, should we really get from cache?
271
- // This is the existing implementation but it could mean this user has no IAMs?
272
-
273
- // Default is using cached IAMs in the messaging controller
341
+ // Dynamically set the retry limit from the header, if not already set
342
+ if (!blockRetryLimit) {
343
+ blockRetryLimit = @([responseHeaders[@" OneSignal-Retry-Limit" ] integerValue ] ?: DEFAULT_RETRY_LIMIT);
344
+ }
345
+
346
+ if ([attempts integerValue ] < [blockRetryLimit integerValue ]) {
347
+ NSInteger nextAttempt = [attempts integerValue ] + 1 ; // Increment attempts
348
+ [self retryAfterDelay: retryAfter
349
+ subscriptionId: subscriptionId
350
+ rywData: rywData
351
+ attempts: @(nextAttempt)
352
+ retryLimit: blockRetryLimit];
353
+ } else {
354
+ // Final attempt without rywToken
355
+ [self fetchInAppMessagesWithoutToken: subscriptionId];
356
+ }
357
+ } else if (code >= 500 && code <= 599 ) {
358
+ [OneSignalLog onesignalLog: ONE_S_LL_VERBOSE message: @" Server error, skipping retries" ];
359
+ [self updateInAppMessagesFromCache ];
360
+ } else {
361
+ [self updateInAppMessagesFromCache ];
362
+ }
363
+ }];
364
+ }
365
+
366
+ - (void )retryAfterDelay : (NSInteger )retryAfter
367
+ subscriptionId : (NSString *)subscriptionId
368
+ rywData : (OSReadYourWriteData *)rywData
369
+ attempts : (NSNumber *)attempts
370
+ retryLimit : (NSNumber *)retryLimit {
371
+
372
+ dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t )(retryAfter * NSEC_PER_SEC)), dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
373
+
374
+ [self attemptFetchWithRetries: subscriptionId
375
+ rywData: rywData
376
+ attempts: attempts
377
+ retryLimit: retryLimit];
378
+ });
379
+ }
380
+
381
+ - (void )fetchInAppMessagesWithoutToken : (NSString *)subscriptionId {
382
+ NSNumber *sessionDuration = @([OSSessionManager.sharedSessionManager getTimeFocusedElapsed ]);
383
+
384
+ OSRequestGetInAppMessages *request = [OSRequestGetInAppMessages withSubscriptionId: subscriptionId
385
+ withSessionDuration: sessionDuration
386
+ withRetryCount: nil
387
+ withRywToken: nil ]; // No retries for the final attempt
388
+
389
+ [OneSignalCoreImpl.sharedClient executeRequest: request
390
+ onSuccess: ^(NSDictionary *result) {
391
+ dispatch_async (dispatch_get_main_queue (), ^{
392
+ [OneSignalLog onesignalLog: ONE_S_LL_VERBOSE message: @" Final attempt without token success" ];
393
+ if (result[@" in_app_messages" ]) {
394
+ NSMutableArray *messages = [NSMutableArray new ];
395
+
396
+ for (NSDictionary *messageJson in result[@" in_app_messages" ]) {
397
+ OSInAppMessageInternal *message = [OSInAppMessageInternal instanceWithJson: messageJson];
398
+ if (message) {
399
+ [messages addObject: message];
400
+ }
401
+ }
402
+
403
+ [self updateInAppMessagesFromServer: messages];
404
+ return ;
405
+ }
274
406
[self updateInAppMessagesFromCache ];
275
407
});
276
408
} onFailure: ^(NSError *error) {
0 commit comments