ADJAttributionHandler.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. //
  2. // ADJAttributionHandler.m
  3. // adjust
  4. //
  5. // Created by Pedro Filipe on 29/10/14.
  6. // Copyright (c) 2014 adjust GmbH. All rights reserved.
  7. //
  8. #import "ADJAttributionHandler.h"
  9. #import "ADJAdjustFactory.h"
  10. #import "ADJUtil.h"
  11. #import "ADJActivityHandler.h"
  12. #import "NSString+ADJAdditions.h"
  13. #import "ADJTimerOnce.h"
  14. #import "ADJPackageBuilder.h"
  15. #import "ADJUtil.h"
  16. static const char * const kInternalQueueName = "com.adjust.AttributionQueue";
  17. static NSString * const kAttributionTimerName = @"Attribution timer";
  18. @interface ADJAttributionHandler()
  19. @property (nonatomic, strong) dispatch_queue_t internalQueue;
  20. @property (nonatomic, strong) ADJRequestHandler *requestHandler;
  21. @property (nonatomic, weak) id<ADJActivityHandler> activityHandler;
  22. @property (nonatomic, weak) id<ADJLogger> logger;
  23. @property (nonatomic, strong) ADJTimerOnce *attributionTimer;
  24. @property (atomic, assign) BOOL paused;
  25. @property (nonatomic, copy) NSString *lastInitiatedBy;
  26. @end
  27. @implementation ADJAttributionHandler
  28. - (id)initWithActivityHandler:(id<ADJActivityHandler>) activityHandler
  29. startsSending:(BOOL)startsSending
  30. userAgent:(NSString *)userAgent
  31. urlStrategy:(ADJUrlStrategy *)urlStrategy
  32. {
  33. self = [super init];
  34. if (self == nil) return nil;
  35. self.internalQueue = dispatch_queue_create(kInternalQueueName, DISPATCH_QUEUE_SERIAL);
  36. self.requestHandler = [[ADJRequestHandler alloc]
  37. initWithResponseCallback:self
  38. urlStrategy:urlStrategy
  39. userAgent:userAgent
  40. requestTimeout:[ADJAdjustFactory requestTimeout]];
  41. self.activityHandler = activityHandler;
  42. self.logger = ADJAdjustFactory.logger;
  43. self.paused = !startsSending;
  44. __weak __typeof__(self) weakSelf = self;
  45. self.attributionTimer = [ADJTimerOnce timerWithBlock:^{
  46. __typeof__(self) strongSelf = weakSelf;
  47. if (strongSelf == nil) return;
  48. [strongSelf requestAttributionI:strongSelf];
  49. }
  50. queue:self.internalQueue
  51. name:kAttributionTimerName];
  52. return self;
  53. }
  54. - (void)checkSessionResponse:(ADJSessionResponseData *)sessionResponseData {
  55. [ADJUtil launchInQueue:self.internalQueue
  56. selfInject:self
  57. block:^(ADJAttributionHandler* selfI) {
  58. [selfI checkSessionResponseI:selfI
  59. sessionResponseData:sessionResponseData];
  60. }];
  61. }
  62. - (void)checkSdkClickResponse:(ADJSdkClickResponseData *)sdkClickResponseData {
  63. [ADJUtil launchInQueue:self.internalQueue
  64. selfInject:self
  65. block:^(ADJAttributionHandler* selfI) {
  66. [selfI checkSdkClickResponseI:selfI
  67. sdkClickResponseData:sdkClickResponseData];
  68. }];
  69. }
  70. - (void)checkAttributionResponse:(ADJAttributionResponseData *)attributionResponseData {
  71. [ADJUtil launchInQueue:self.internalQueue
  72. selfInject:self
  73. block:^(ADJAttributionHandler* selfI) {
  74. [selfI checkAttributionResponseI:selfI
  75. attributionResponseData:attributionResponseData];
  76. }];
  77. }
  78. - (void)getAttribution {
  79. [ADJUtil launchInQueue:self.internalQueue
  80. selfInject:self
  81. block:^(ADJAttributionHandler* selfI) {
  82. selfI.lastInitiatedBy = @"sdk";
  83. [selfI waitRequestAttributionWithDelayI:selfI
  84. milliSecondsDelay:0];
  85. }];
  86. }
  87. - (void)pauseSending {
  88. self.paused = YES;
  89. }
  90. - (void)resumeSending {
  91. self.paused = NO;
  92. }
  93. #pragma mark - internal
  94. - (void)checkSessionResponseI:(ADJAttributionHandler*)selfI
  95. sessionResponseData:(ADJSessionResponseData *)sessionResponseData {
  96. [selfI checkAttributionI:selfI responseData:sessionResponseData];
  97. [selfI.activityHandler launchSessionResponseTasks:sessionResponseData];
  98. }
  99. - (void)checkSdkClickResponseI:(ADJAttributionHandler*)selfI
  100. sdkClickResponseData:(ADJSdkClickResponseData *)sdkClickResponseData {
  101. [selfI checkAttributionI:selfI responseData:sdkClickResponseData];
  102. [selfI.activityHandler launchSdkClickResponseTasks:sdkClickResponseData];
  103. }
  104. - (void)checkAttributionResponseI:(ADJAttributionHandler*)selfI
  105. attributionResponseData:(ADJAttributionResponseData *)attributionResponseData {
  106. [selfI checkAttributionI:selfI responseData:attributionResponseData];
  107. [selfI checkDeeplinkI:selfI attributionResponseData:attributionResponseData];
  108. [selfI.activityHandler launchAttributionResponseTasks:attributionResponseData];
  109. }
  110. - (void)checkAttributionI:(ADJAttributionHandler*)selfI
  111. responseData:(ADJResponseData *)responseData {
  112. if (responseData.jsonResponse == nil) {
  113. return;
  114. }
  115. NSNumber *timerMilliseconds = [responseData.jsonResponse objectForKey:@"ask_in"];
  116. if (timerMilliseconds != nil) {
  117. [selfI.activityHandler setAskingAttribution:YES];
  118. selfI.lastInitiatedBy = @"backend";
  119. [selfI waitRequestAttributionWithDelayI:selfI
  120. milliSecondsDelay:[timerMilliseconds intValue]];
  121. return;
  122. }
  123. [selfI.activityHandler setAskingAttribution:NO];
  124. NSDictionary * jsonAttribution = [responseData.jsonResponse objectForKey:@"attribution"];
  125. responseData.attribution = [ADJAttribution dataWithJsonDict:jsonAttribution adid:responseData.adid];
  126. }
  127. - (void)checkDeeplinkI:(ADJAttributionHandler*)selfI
  128. attributionResponseData:(ADJAttributionResponseData *)attributionResponseData {
  129. if (attributionResponseData.jsonResponse == nil) {
  130. return;
  131. }
  132. NSDictionary * jsonAttribution = [attributionResponseData.jsonResponse objectForKey:@"attribution"];
  133. if (jsonAttribution == nil) {
  134. return;
  135. }
  136. NSString *deepLink = [jsonAttribution objectForKey:@"deeplink"];
  137. if (deepLink == nil) {
  138. return;
  139. }
  140. attributionResponseData.deeplink = [NSURL URLWithString:deepLink];
  141. }
  142. - (void)requestAttributionI:(ADJAttributionHandler*)selfI {
  143. if (selfI.paused) {
  144. [selfI.logger debug:@"Attribution handler is paused"];
  145. return;
  146. }
  147. if ([selfI.activityHandler isGdprForgotten]) {
  148. [selfI.logger debug:@"Attribution request won't be fired for forgotten user"];
  149. return;
  150. }
  151. ADJActivityPackage* attributionPackage = [selfI buildAndGetAttributionPackageI:selfI];
  152. [selfI.logger verbose:@"%@", attributionPackage.extendedString];
  153. NSDictionary *sendingParameters = @{
  154. @"sent_at": [ADJUtil formatSeconds1970:[NSDate.date timeIntervalSince1970]]
  155. };
  156. [selfI.requestHandler sendPackageByGET:attributionPackage
  157. sendingParameters:sendingParameters];
  158. }
  159. - (void)responseCallback:(ADJResponseData *)responseData {
  160. if (responseData.jsonResponse) {
  161. [self.logger debug:
  162. @"Got attribution JSON response with message: %@", responseData.message];
  163. } else {
  164. [self.logger error:
  165. @"Could not get attribution JSON response with message: %@", responseData.message];
  166. }
  167. // Check if any package response contains information that user has opted out.
  168. // If yes, disable SDK and flush any potentially stored packages that happened afterwards.
  169. if (responseData.trackingState == ADJTrackingStateOptedOut) {
  170. [self.activityHandler setTrackingStateOptedOut];
  171. return;
  172. }
  173. if ([responseData isKindOfClass:[ADJAttributionResponseData class]]) {
  174. [self checkAttributionResponse:(ADJAttributionResponseData*)responseData];
  175. }
  176. }
  177. - (void)waitRequestAttributionWithDelayI:(ADJAttributionHandler*)selfI
  178. milliSecondsDelay:(int)milliSecondsDelay {
  179. NSTimeInterval secondsDelay = milliSecondsDelay / 1000;
  180. NSTimeInterval nextAskIn = [selfI.attributionTimer fireIn];
  181. if (nextAskIn > secondsDelay) {
  182. return;
  183. }
  184. if (milliSecondsDelay > 0) {
  185. [selfI.logger debug:@"Waiting to query attribution in %d milliseconds", milliSecondsDelay];
  186. }
  187. // set the new time the timer will fire in
  188. [selfI.attributionTimer startIn:secondsDelay];
  189. }
  190. - (ADJActivityPackage *)buildAndGetAttributionPackageI:(ADJAttributionHandler*)selfI
  191. {
  192. double now = [NSDate.date timeIntervalSince1970];
  193. ADJPackageBuilder *attributionBuilder = [[ADJPackageBuilder alloc]
  194. initWithDeviceInfo:selfI.activityHandler.deviceInfo
  195. activityState:selfI.activityHandler.activityState
  196. config:selfI.activityHandler.adjustConfig
  197. sessionParameters:selfI.activityHandler.sessionParameters
  198. trackingStatusManager:selfI.activityHandler.trackingStatusManager
  199. createdAt:now];
  200. ADJActivityPackage *attributionPackage = [attributionBuilder buildAttributionPackage:selfI.lastInitiatedBy];
  201. selfI.lastInitiatedBy = nil;
  202. return attributionPackage;
  203. }
  204. #pragma mark - private
  205. - (void)teardown {
  206. [ADJAdjustFactory.logger verbose:@"ADJAttributionHandler teardown"];
  207. if (self.attributionTimer != nil) {
  208. [self.attributionTimer cancel];
  209. }
  210. self.internalQueue = nil;
  211. self.activityHandler = nil;
  212. self.logger = nil;
  213. self.attributionTimer = nil;
  214. self.requestHandler = nil;
  215. }
  216. @end