Skip to content

Commit

Permalink
OSMessagingController Retry Logic
Browse files Browse the repository at this point in the history
Motivation: retry logic should work as follows:
1. initial request with offset --> failure
2. get retry limit & retry after from response & start retrying
3. if hit limit, make one final request with the offset: 0

The final request tells the backend, just show me what you got.

Example: If the retry limit is 3 & we never get a successful response retrying, we should end up with 5 total requests.
  • Loading branch information
Rodrigo Gomez Palacio committed Sep 27, 2024
1 parent 990bb73 commit 4fbe142
Showing 1 changed file with 118 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Modified MIT License
*
* Copyright 2017 OneSignal
* Copyright 2024 OneSignal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -35,8 +35,13 @@
#import "OSInAppMessagePrompt.h"
#import "OSInAppMessagingRequests.h"
#import "OneSignalWebViewManager.h"
#import "OneSignalTracker.h"
#import <OneSignalOutcomes/OneSignalOutcomes.h>
#import "OSSessionManager.h"
#import "OneSignalOSCore/OneSignalOSCore-Swift.h"

static NSInteger const DEFAULT_RETRY_AFTER_SECONDS = 1; // Default 1 second retry delay
static NSInteger const DEFAULT_RETRY_LIMIT = 3; // Default retry limit

@implementation OSInAppMessageWillDisplayEvent

Expand Down Expand Up @@ -248,16 +253,48 @@ - (void)getInAppMessagesFromServer:(NSString *)subscriptionId {
[self updateInAppMessagesFromCache];
return;
}

OSRequestGetInAppMessages *request = [OSRequestGetInAppMessages withSubscriptionId:subscriptionId];
[OneSignalCoreImpl.sharedClient executeRequest:request onSuccess:^(NSDictionary *result) {

OSConsistencyManager *consistencyManager = [OSConsistencyManager shared];
NSString *onesignalId = OneSignalUserManagerImpl.sharedInstance.onesignalId;

if (!onesignalId) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"Failed to get in app messages due to no OneSignal ID"];
return;
}

OSIamFetchReadyCondition *condition = [[OSIamFetchReadyCondition alloc] initWithId:onesignalId];
NSString *newestOffset = [consistencyManager registerCondition:condition forId:onesignalId];

NSNumber *sessionDuration = @([OSSessionManager getTimeFocusedElapsed]);

// Initial request
[self attemptFetchWithRetries:subscriptionId
rywToken:newestOffset
sessionDuration:sessionDuration
attempts:1];
}

- (void)attemptFetchWithRetries:(NSString *)subscriptionId
rywToken:(NSString *)rywToken
sessionDuration:(NSNumber *)sessionDuration
attempts:(NSInteger)attempts {

NSInteger retryCount = (attempts > 1) ? (attempts - 1) : 0;

OSRequestGetInAppMessages *request = [OSRequestGetInAppMessages withSubscriptionId:subscriptionId
withSessionDuration:sessionDuration
withRetryCount:@(retryCount)
withRywToken:rywToken];

[OneSignalCoreImpl.sharedClient executeRequest:request
onSuccess:^(NSDictionary *result) {
dispatch_async(dispatch_get_main_queue(), ^{
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"getInAppMessagesFromServer success"];
if (result[@"in_app_messages"]) { // when there are no IAMs, will this still be there?
let messages = [NSMutableArray new];
if (result[@"in_app_messages"]) {
NSMutableArray *messages = [NSMutableArray new];

for (NSDictionary *messageJson in result[@"in_app_messages"]) {
let message = [OSInAppMessageInternal instanceWithJson:messageJson];
OSInAppMessageInternal *message = [OSInAppMessageInternal instanceWithJson:messageJson];
if (message) {
[messages addObject:message];
}
Expand All @@ -266,11 +303,80 @@ - (void)getInAppMessagesFromServer:(NSString *)subscriptionId {
[self updateInAppMessagesFromServer:messages];
return;
}

// TODO: Check this request and response. If no IAMs returned, should we really get from cache?
// This is the existing implementation but it could mean this user has no IAMs?

// Default is using cached IAMs in the messaging controller
[self updateInAppMessagesFromCache];
});
}
onFailure:^(NSError *error) {
NSDictionary *errorInfo = error.userInfo[@"returned"];
NSNumber *statusCode = errorInfo[@"httpStatusCode"];

if (!statusCode) {
[self updateInAppMessagesFromCache];
return;
}

[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"getInAppMessagesFromServer failure: %@", error.localizedDescription]];

NSInteger code = [statusCode integerValue];
if (code == 425 || code == 429) { // 425 Too Early or 429 Too Many Requests
NSInteger retryAfter = [errorInfo[@"OneSignal-Retry-After"] integerValue] ?: DEFAULT_RETRY_AFTER_SECONDS;
NSInteger retryLimit = [errorInfo[@"OneSignal-Retry-Limit"] integerValue] ?: DEFAULT_RETRY_LIMIT;

if (attempts <= retryLimit) {
[self retryAfterDelay:retryAfter
subscriptionId:subscriptionId
rywToken:rywToken
sessionDuration:sessionDuration
attempts:attempts];
} else {
// Final attempt with offset = 0
[self fetchInAppMessagesWithoutOffset:subscriptionId sessionDuration:sessionDuration];
}
} else {
[self updateInAppMessagesFromCache];
}
}];
}

- (void)retryAfterDelay:(NSInteger)retryAfter
subscriptionId:(NSString *)subscriptionId
rywToken:(NSString *)rywToken
sessionDuration:(NSNumber *)sessionDuration
attempts:(NSInteger)attempts {

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryAfter * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self attemptFetchWithRetries:subscriptionId
rywToken:rywToken
sessionDuration:sessionDuration
attempts:attempts + 1]; // No more delayTime argument
});
}

- (void)fetchInAppMessagesWithoutOffset:(NSString *)subscriptionId
sessionDuration:(NSNumber *)sessionDuration {

OSRequestGetInAppMessages *request = [OSRequestGetInAppMessages withSubscriptionId:subscriptionId
withSessionDuration:sessionDuration
withRetryCount:@0
withRywToken:nil]; // No retries for the final attempt

[OneSignalCoreImpl.sharedClient executeRequest:request
onSuccess:^(NSDictionary *result) {
dispatch_async(dispatch_get_main_queue(), ^{
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"Final attempt without offset success"];
if (result[@"in_app_messages"]) {
NSMutableArray *messages = [NSMutableArray new];

for (NSDictionary *messageJson in result[@"in_app_messages"]) {
OSInAppMessageInternal *message = [OSInAppMessageInternal instanceWithJson:messageJson];
if (message) {
[messages addObject:message];
}
}

[self updateInAppMessagesFromServer:messages];
return;
}
[self updateInAppMessagesFromCache];
});
} onFailure:^(NSError *error) {
Expand Down

0 comments on commit 4fbe142

Please sign in to comment.