PINURLSessionManager.m 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. //
  2. // PINURLSessionManager.m
  3. // Pods
  4. //
  5. // Created by Garrett Moon on 6/26/15.
  6. //
  7. //
  8. #import "PINURLSessionManager.h"
  9. NSString * const PINURLErrorDomain = @"PINURLErrorDomain";
  10. @interface PINURLSessionManager () <NSURLSessionDelegate, NSURLSessionDataDelegate>
  11. @property (nonatomic, strong) NSLock *sessionManagerLock;
  12. @property (nonatomic, strong) NSURLSession *session;
  13. @property (nonatomic, strong) NSOperationQueue *operationQueue;
  14. @property (nonatomic, strong) NSMutableDictionary <NSNumber *, dispatch_queue_t> *delegateQueues;
  15. @property (nonatomic, strong) NSMutableDictionary *completions;
  16. @end
  17. @implementation PINURLSessionManager
  18. - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration
  19. {
  20. if (self = [super init]) {
  21. self.sessionManagerLock = [[NSLock alloc] init];
  22. self.sessionManagerLock.name = @"PINURLSessionManager";
  23. self.operationQueue = [[NSOperationQueue alloc] init];
  24. self.operationQueue.name = @"PINURLSessionManager Operation Queue";
  25. //queue must be serial to ensure proper ordering
  26. [self.operationQueue setMaxConcurrentOperationCount:1];
  27. self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:self.operationQueue];
  28. self.completions = [[NSMutableDictionary alloc] init];
  29. self.delegateQueues = [[NSMutableDictionary alloc] init];
  30. }
  31. return self;
  32. }
  33. - (void)invalidateSessionAndCancelTasks
  34. {
  35. [self lock];
  36. [self.session invalidateAndCancel];
  37. [self unlock];
  38. }
  39. - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLResponse *response, NSError *error))completionHandler
  40. {
  41. [self lock];
  42. NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
  43. if (completionHandler) {
  44. [self.completions setObject:completionHandler forKey:@(dataTask.taskIdentifier)];
  45. }
  46. NSString *queueName = [NSString stringWithFormat:@"PINURLSessionManager delegate queue - %ld", (unsigned long)dataTask.taskIdentifier];
  47. dispatch_queue_t delegateQueue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL);
  48. [self.delegateQueues setObject:delegateQueue forKey:@(dataTask.taskIdentifier)];
  49. [self unlock];
  50. return dataTask;
  51. }
  52. - (void)lock
  53. {
  54. [self.sessionManagerLock lock];
  55. }
  56. - (void)unlock
  57. {
  58. [self.sessionManagerLock unlock];
  59. }
  60. #pragma mark NSURLSessionDataDelegate
  61. - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
  62. {
  63. if ([self.delegate respondsToSelector:@selector(didReceiveAuthenticationChallenge:forTask:completionHandler:)]) {
  64. [self.delegate didReceiveAuthenticationChallenge:challenge forTask:nil completionHandler:completionHandler];
  65. } else {
  66. //Even though this is documented to be non-nil, in the wild it sometimes is
  67. if (completionHandler) {
  68. completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
  69. }
  70. }
  71. }
  72. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
  73. {
  74. [self lock];
  75. dispatch_queue_t delegateQueue = self.delegateQueues[@(task.taskIdentifier)];
  76. [self unlock];
  77. __weak typeof(self) weakSelf = self;
  78. dispatch_async(delegateQueue, ^{
  79. typeof(self) strongSelf = weakSelf;
  80. if ([strongSelf.delegate respondsToSelector:@selector(didReceiveAuthenticationChallenge:forTask:completionHandler:)]) {
  81. [strongSelf.delegate didReceiveAuthenticationChallenge:challenge forTask:task completionHandler:completionHandler];
  82. } else {
  83. //Even though this is documented to be non-nil, in the wild it sometimes is
  84. if (completionHandler) {
  85. completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
  86. }
  87. }
  88. });
  89. }
  90. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
  91. {
  92. [self lock];
  93. dispatch_queue_t delegateQueue = self.delegateQueues[@(dataTask.taskIdentifier)];
  94. [self unlock];
  95. __weak typeof(self) weakSelf = self;
  96. dispatch_async(delegateQueue, ^{
  97. typeof(self) strongSelf = weakSelf;
  98. [strongSelf.delegate didReceiveData:data forTask:dataTask];
  99. });
  100. }
  101. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
  102. {
  103. [self lock];
  104. dispatch_queue_t delegateQueue = self.delegateQueues[@(task.taskIdentifier)];
  105. [self unlock];
  106. if (!error && [task.response isKindOfClass:[NSHTTPURLResponse class]]) {
  107. NSInteger statusCode = [(NSHTTPURLResponse *)task.response statusCode];
  108. if (statusCode >= 400) {
  109. error = [NSError errorWithDomain:PINURLErrorDomain
  110. code:statusCode
  111. userInfo:@{NSLocalizedDescriptionKey : @"HTTP Error Response."}];
  112. }
  113. }
  114. __weak typeof(self) weakSelf = self;
  115. dispatch_async(delegateQueue, ^{
  116. typeof(self) strongSelf = weakSelf;
  117. [strongSelf.delegate didCompleteTask:task withError:error];
  118. [strongSelf lock];
  119. void (^completionHandler)(NSURLResponse *, NSError *) = strongSelf.completions[@(task.taskIdentifier)];
  120. [strongSelf.completions removeObjectForKey:@(task.taskIdentifier)];
  121. [strongSelf.delegateQueues removeObjectForKey:@(task.taskIdentifier)];
  122. [strongSelf unlock];
  123. if (completionHandler) {
  124. completionHandler(task.response, error);
  125. }
  126. });
  127. }
  128. @end