10
10
#import " ADJUtil.h"
11
11
#import " ADJLogger.h"
12
12
#import " ADJAdjustFactory.h"
13
- #import " ADJBackoffStrategy.h"
14
13
#import " ADJUserDefaults.h"
15
14
#import " ADJPackageBuilder.h"
16
15
#import " ADJPurchaseVerificationResult.h"
@@ -24,12 +23,11 @@ @interface ADJPurchaseVerificationHandler()
24
23
@property (nonatomic , strong ) ADJRequestHandler *requestHandler;
25
24
26
25
@property (nonatomic , assign ) BOOL paused;
27
- @property (nonatomic , strong ) ADJBackoffStrategy *backoffStrategy ;
26
+ @property (nonatomic , assign ) BOOL isSendingPurchaseVerificationPackage ;
28
27
29
28
@property (nonatomic , weak ) id <ADJLogger> logger;
30
29
@property (nonatomic , weak ) id <ADJActivityHandler> activityHandler;
31
30
32
- @property (nonatomic , assign ) NSInteger lastPackageRetriesCount;
33
31
@property (nonatomic , strong ) NSNumber *lastPackageRetryInMilli;
34
32
35
33
@end
@@ -48,12 +46,12 @@ - (id)initWithActivityHandler:(id<ADJActivityHandler>)activityHandler
48
46
49
47
self.internalQueue = dispatch_queue_create (kInternalQueueName , DISPATCH_QUEUE_SERIAL);
50
48
self.logger = ADJAdjustFactory.logger ;
51
- self.lastPackageRetriesCount = 0 ;
52
49
53
- self.requestHandler = [[ADJRequestHandler alloc ] initWithResponseCallback: self
54
- urlStrategy: urlStrategy
55
- requestTimeout: [ADJAdjustFactory requestTimeout ]
56
- adjustConfiguration: activityHandler.adjustConfig];
50
+ self.requestHandler =
51
+ [[ADJRequestHandler alloc ] initWithResponseCallback: self
52
+ urlStrategy: urlStrategy
53
+ requestTimeout: [ADJAdjustFactory verifyRequestTimeout ]
54
+ adjustConfiguration: activityHandler.adjustConfig];
57
55
58
56
[ADJUtil launchInQueue: self .internalQueue
59
57
selfInject: self
@@ -68,6 +66,8 @@ - (void)pauseSending {
68
66
selfInject: self
69
67
block: ^(ADJPurchaseVerificationHandler *selfI) {
70
68
selfI.paused = YES ;
69
+ selfI.isSendingPurchaseVerificationPackage = NO ;
70
+ selfI.lastPackageRetryInMilli = nil ;
71
71
}];
72
72
}
73
73
@@ -114,9 +114,10 @@ - (void)teardown {
114
114
115
115
self.internalQueue = nil ;
116
116
self.logger = nil ;
117
- self.backoffStrategy = nil ;
118
117
self.packageQueue = nil ;
119
118
self.activityHandler = nil ;
119
+ self.isSendingPurchaseVerificationPackage = NO ;
120
+ self.lastPackageRetryInMilli = nil ;
120
121
}
121
122
122
123
#pragma mark - Private & helper methods
@@ -126,7 +127,8 @@ - (void)initI:(ADJPurchaseVerificationHandler *)selfI
126
127
startsSending : (BOOL )startsSending {
127
128
selfI.activityHandler = activityHandler;
128
129
selfI.paused = !startsSending;
129
- selfI.backoffStrategy = [ADJAdjustFactory sdkClickHandlerBackoffStrategy ];
130
+ selfI.isSendingPurchaseVerificationPackage = NO ;
131
+ selfI.lastPackageRetryInMilli = nil ;
130
132
selfI.packageQueue = [NSMutableArray array ];
131
133
}
132
134
@@ -143,63 +145,47 @@ - (void)sendNextPurchaseVerificationPackageI:(ADJPurchaseVerificationHandler *)s
143
145
[selfI.logger debug: @" Purchase verification handler is paused" ];
144
146
return ;
145
147
}
146
- NSUInteger queueSize = selfI.packageQueue .count ;
147
- if (queueSize == 0 ) {
148
+ if (selfI.isSendingPurchaseVerificationPackage ) {
149
+ [selfI.logger debug: @" Purchase verification handler is already sending a package" ];
150
+ return ;
151
+ }
152
+ if (selfI.packageQueue .count == 0 ) {
148
153
return ;
149
154
}
150
155
if ([selfI.activityHandler isGdprForgotten ]) {
151
- [selfI.logger debug: @" purchase_verification request won't be fired for forgotten user" ];
156
+ [selfI.logger debug: @" purchase_verification request won't be sent for GDPR forgotten user" ];
157
+ return ;
158
+ }
159
+
160
+ // check if we need to wait for backend-requested retry_in delay
161
+ NSNumber *waitTime = [selfI waitTimeTimeInterval ];
162
+ if (waitTime != nil ) {
163
+ dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t )([waitTime doubleValue ] * NSEC_PER_SEC)),
164
+ selfI.internalQueue , ^{
165
+ // clear the retry delay after waiting
166
+ selfI.lastPackageRetryInMilli = nil ;
167
+ [selfI sendNextPurchaseVerificationPackage ];
168
+ });
152
169
return ;
153
170
}
154
171
172
+ // get the package but keep it in the queue until processing is complete
155
173
ADJActivityPackage *purchaseVerificationPackage = [self .packageQueue objectAtIndex: 0 ];
156
- [self .packageQueue removeObjectAtIndex: 0 ];
157
174
158
175
if (![purchaseVerificationPackage isKindOfClass: [ADJActivityPackage class ]]) {
159
176
[selfI.logger error: @" Failed to read purchase_verification package" ];
177
+ // remove the bad package to prevent infinite loop
178
+ [selfI.packageQueue removeObjectAtIndex: 0 ];
179
+ selfI.isSendingPurchaseVerificationPackage = NO ;
160
180
[selfI sendNextPurchaseVerificationPackage ];
161
181
return ;
162
182
}
163
183
164
- dispatch_block_t work = ^{
165
- [selfI.requestHandler sendPackageByPOST: purchaseVerificationPackage
166
- sendingParameters: nil ];
167
- [selfI sendNextPurchaseVerificationPackage ];
168
- };
169
-
170
- NSNumber *waitTimeSecondsDouble = [selfI waitTimeTimeInterval ];
171
-
172
- if (waitTimeSecondsDouble != nil ) {
173
- dispatch_after (dispatch_time (DISPATCH_TIME_NOW,
174
- (int64_t )(waitTimeSecondsDouble.doubleValue * NSEC_PER_SEC)),
175
- self.internalQueue , work);
176
- } else {
177
- work ();
178
- }
179
- }
180
- - (NSNumber *)waitTimeTimeInterval {
181
- if (self.lastPackageRetriesCount > 0 ) {
182
- NSTimeInterval waitTime = [ADJUtil waitingTime: self .lastPackageRetriesCount
183
- backoffStrategy: self .backoffStrategy];
184
-
185
- [self .logger verbose:
186
- @" Waiting for %@ seconds before retrying purchase_verification for the %d time" ,
187
- [ADJUtil secondsNumberFormat: waitTime], self .lastPackageRetriesCount];
188
-
189
- return @(waitTime);
190
- }
191
-
192
- if (self.lastPackageRetryInMilli != nil ) {
193
- NSTimeInterval waitTime = [self .lastPackageRetryInMilli intValue ] / 1000.0 ;
194
-
195
- [self .logger verbose:
196
- @" Waiting for %@ seconds before retrying purchase_verification with retry_in" ,
197
- [ADJUtil secondsNumberFormat: waitTime]];
198
-
199
- return @(waitTime);
200
- }
184
+ // set flag to indicate we're sending a package
185
+ selfI.isSendingPurchaseVerificationPackage = YES ;
201
186
202
- return nil ;
187
+ [selfI.requestHandler sendPackageByPOST: purchaseVerificationPackage
188
+ sendingParameters: nil ];
203
189
}
204
190
205
191
- (void )updatePackagesTrackingI : (ADJPurchaseVerificationHandler *)selfI
@@ -220,50 +206,65 @@ - (void)updatePackagesTrackingI:(ADJPurchaseVerificationHandler *)selfI
220
206
}
221
207
222
208
- (void )responseCallback : (ADJResponseData *)responseData {
209
+ // reset flag to indicate we're done processing this package
210
+ self.isSendingPurchaseVerificationPackage = NO ;
211
+
223
212
if (!responseData.jsonResponse ) {
224
213
[self .logger error:
225
214
@" Could not get purchase_verification JSON response with message: %@ " , responseData.message];
226
215
ADJPurchaseVerificationResult *verificationResult = [[ADJPurchaseVerificationResult alloc ] init ];
227
216
verificationResult.verificationStatus = @" not_verified" ;
228
217
verificationResult.code = 102 ;
229
218
verificationResult.message = responseData.message ;
230
- responseData.purchaseVerificationPackage .purchaseVerificationCallback (verificationResult);
231
- }
232
- // Check if any package response contains information that user has opted out.
233
- // If yes, disable SDK and flush any potentially stored packages that happened afterwards.
234
- if (responseData.trackingState == ADJTrackingStateOptedOut) {
235
- self.lastPackageRetriesCount = 0 ;
219
+ ((ADJPurchaseVerificationResponseData *)responseData).error = verificationResult;
220
+ } else {
221
+ // check if any package response contains information that user has opted out.
222
+ // if yes, disable SDK and flush any potentially stored packages that happened afterwards.
223
+ if (responseData.trackingState == ADJTrackingStateOptedOut) {
224
+ [self .activityHandler setTrackingStateOptedOut ];
225
+ return ;
226
+ }
227
+
228
+ // check if backend requested retry_in delay
229
+ if (responseData.retryInMilli != nil ) {
230
+ self.lastPackageRetryInMilli = responseData.retryInMilli ;
231
+ [self .logger error: @" Retrying purchase_verification package with retry in %d ms" ,
232
+ [responseData.retryInMilli intValue ]];
233
+
234
+ // package stays in queue - schedule retry
235
+ [self sendNextPurchaseVerificationPackage ];
236
+ return ;
237
+ }
238
+
239
+ // reset retry counter after successful response
236
240
self.lastPackageRetryInMilli = nil ;
237
- [self .activityHandler setTrackingStateOptedOut ];
238
- return ;
239
241
}
240
242
241
- if ([ self retryPackageWithResponse: responseData]) {
242
- [ self sendPurchaseVerificationPackage: responseData.purchaseVerificationPackage];
243
- return ;
243
+ // processing is complete - remove the package from queue
244
+ if (self. packageQueue . count > 0 ) {
245
+ [ self .packageQueue removeObjectAtIndex: 0 ] ;
244
246
}
245
247
246
- self.lastPackageRetriesCount = 0 ;
247
- self.lastPackageRetryInMilli = nil ;
248
+ // finish package tracking without retrying / backoff
248
249
[self .activityHandler finishedTracking: responseData];
250
+
251
+ // process next package in queue if any
252
+ [self sendNextPurchaseVerificationPackage ];
249
253
}
250
254
251
- - (BOOL )retryPackageWithResponse : (ADJResponseData *)responseData {
252
- if (responseData.jsonResponse == nil ) {
253
- self.lastPackageRetriesCount ++;
254
- [self .logger error: @" Retrying purchase_verification package for the %d time" ,
255
- self .lastPackageRetriesCount];
256
- return YES ;
257
- }
255
+ - (NSNumber *)waitTimeTimeInterval {
256
+ // handle backend-requested retry_in delay
257
+ if (self.lastPackageRetryInMilli != nil ) {
258
+ NSTimeInterval waitTime = [self .lastPackageRetryInMilli intValue ] / 1000.0 ;
258
259
259
- if (responseData. retryInMilli != nil ) {
260
- self. lastPackageRetryInMilli = responseData. retryInMilli ;
261
- [ self .logger error: @" Retrying purchase_verification package with retry in %d ms " ,
262
- [responseData.retryInMilli intValue ]];
263
- return YES ;
260
+ [ self .logger verbose:
261
+ @" Waiting for %@ seconds before retrying purchase_verification with retry_in " ,
262
+ [ADJUtil secondsNumberFormat: waitTime]];
263
+
264
+ return @(waitTime) ;
264
265
}
265
266
266
- return NO ;
267
+ return nil ;
267
268
}
268
269
269
270
@end
0 commit comments