New file |
| | |
| | | // AFURLSessionManager.m |
| | | // Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) |
| | | // |
| | | // Permission is hereby granted, free of charge, to any person obtaining a copy |
| | | // of this software and associated documentation files (the "Software"), to deal |
| | | // in the Software without restriction, including without limitation the rights |
| | | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| | | // copies of the Software, and to permit persons to whom the Software is |
| | | // furnished to do so, subject to the following conditions: |
| | | // |
| | | // The above copyright notice and this permission notice shall be included in |
| | | // all copies or substantial portions of the Software. |
| | | // |
| | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| | | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| | | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| | | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| | | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| | | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| | | // THE SOFTWARE. |
| | | |
| | | #import "AFURLSessionManager.h" |
| | | #import <objc/runtime.h> |
| | | |
| | | #ifndef NSFoundationVersionNumber_iOS_8_0 |
| | | #define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11 |
| | | #else |
| | | #define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0 |
| | | #endif |
| | | |
| | | static dispatch_queue_t url_session_manager_creation_queue() { |
| | | static dispatch_queue_t af_url_session_manager_creation_queue; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); |
| | | }); |
| | | |
| | | return af_url_session_manager_creation_queue; |
| | | } |
| | | |
| | | static void url_session_manager_create_task_safely(dispatch_block_t block) { |
| | | if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) { |
| | | // Fix of bug |
| | | // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8) |
| | | // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093 |
| | | dispatch_sync(url_session_manager_creation_queue(), block); |
| | | } else { |
| | | block(); |
| | | } |
| | | } |
| | | |
| | | static dispatch_queue_t url_session_manager_processing_queue() { |
| | | static dispatch_queue_t af_url_session_manager_processing_queue; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT); |
| | | }); |
| | | |
| | | return af_url_session_manager_processing_queue; |
| | | } |
| | | |
| | | static dispatch_group_t url_session_manager_completion_group() { |
| | | static dispatch_group_t af_url_session_manager_completion_group; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | af_url_session_manager_completion_group = dispatch_group_create(); |
| | | }); |
| | | |
| | | return af_url_session_manager_completion_group; |
| | | } |
| | | |
| | | NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume"; |
| | | NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete"; |
| | | NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend"; |
| | | NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate"; |
| | | NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error"; |
| | | |
| | | NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; |
| | | NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; |
| | | NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; |
| | | NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error"; |
| | | NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; |
| | | |
| | | static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock"; |
| | | |
| | | static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3; |
| | | |
| | | typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error); |
| | | typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); |
| | | |
| | | typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request); |
| | | typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); |
| | | typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); |
| | | |
| | | typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task); |
| | | typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend); |
| | | typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error); |
| | | |
| | | typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response); |
| | | typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); |
| | | typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); |
| | | typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse); |
| | | |
| | | typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location); |
| | | typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite); |
| | | typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes); |
| | | typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *); |
| | | |
| | | typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error); |
| | | |
| | | |
| | | #pragma mark - |
| | | |
| | | @interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate> |
| | | - (instancetype)initWithTask:(NSURLSessionTask *)task; |
| | | @property (nonatomic, weak) AFURLSessionManager *manager; |
| | | @property (nonatomic, strong) NSMutableData *mutableData; |
| | | @property (nonatomic, strong) NSProgress *uploadProgress; |
| | | @property (nonatomic, strong) NSProgress *downloadProgress; |
| | | @property (nonatomic, copy) NSURL *downloadFileURL; |
| | | @property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; |
| | | @property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; |
| | | @property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; |
| | | @property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; |
| | | @end |
| | | |
| | | @implementation AFURLSessionManagerTaskDelegate |
| | | |
| | | - (instancetype)initWithTask:(NSURLSessionTask *)task { |
| | | self = [super init]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | _mutableData = [NSMutableData data]; |
| | | _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; |
| | | _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; |
| | | |
| | | __weak __typeof__(task) weakTask = task; |
| | | for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ]) |
| | | { |
| | | progress.totalUnitCount = NSURLSessionTransferSizeUnknown; |
| | | progress.cancellable = YES; |
| | | progress.cancellationHandler = ^{ |
| | | [weakTask cancel]; |
| | | }; |
| | | progress.pausable = YES; |
| | | progress.pausingHandler = ^{ |
| | | [weakTask suspend]; |
| | | }; |
| | | #if AF_CAN_USE_AT_AVAILABLE |
| | | if (@available(iOS 9, macOS 10.11, *)) |
| | | #else |
| | | if ([progress respondsToSelector:@selector(setResumingHandler:)]) |
| | | #endif |
| | | { |
| | | progress.resumingHandler = ^{ |
| | | [weakTask resume]; |
| | | }; |
| | | } |
| | | |
| | | [progress addObserver:self |
| | | forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) |
| | | options:NSKeyValueObservingOptionNew |
| | | context:NULL]; |
| | | } |
| | | return self; |
| | | } |
| | | |
| | | - (void)dealloc { |
| | | [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; |
| | | [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; |
| | | } |
| | | |
| | | #pragma mark - NSProgress Tracking |
| | | |
| | | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { |
| | | if ([object isEqual:self.downloadProgress]) { |
| | | if (self.downloadProgressBlock) { |
| | | self.downloadProgressBlock(object); |
| | | } |
| | | } |
| | | else if ([object isEqual:self.uploadProgress]) { |
| | | if (self.uploadProgressBlock) { |
| | | self.uploadProgressBlock(object); |
| | | } |
| | | } |
| | | } |
| | | |
| | | #pragma mark - NSURLSessionTaskDelegate |
| | | |
| | | - (void)URLSession:(__unused NSURLSession *)session |
| | | task:(NSURLSessionTask *)task |
| | | didCompleteWithError:(NSError *)error |
| | | { |
| | | __strong AFURLSessionManager *manager = self.manager; |
| | | |
| | | __block id responseObject = nil; |
| | | |
| | | __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; |
| | | userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; |
| | | |
| | | //Performance Improvement from #2672 |
| | | NSData *data = nil; |
| | | if (self.mutableData) { |
| | | data = [self.mutableData copy]; |
| | | //We no longer need the reference, so nil it out to gain back some memory. |
| | | self.mutableData = nil; |
| | | } |
| | | |
| | | if (self.downloadFileURL) { |
| | | userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL; |
| | | } else if (data) { |
| | | userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data; |
| | | } |
| | | |
| | | if (error) { |
| | | userInfo[AFNetworkingTaskDidCompleteErrorKey] = error; |
| | | |
| | | dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ |
| | | if (self.completionHandler) { |
| | | self.completionHandler(task.response, responseObject, error); |
| | | } |
| | | |
| | | dispatch_async(dispatch_get_main_queue(), ^{ |
| | | [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; |
| | | }); |
| | | }); |
| | | } else { |
| | | dispatch_async(url_session_manager_processing_queue(), ^{ |
| | | NSError *serializationError = nil; |
| | | responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]; |
| | | |
| | | if (self.downloadFileURL) { |
| | | responseObject = self.downloadFileURL; |
| | | } |
| | | |
| | | if (responseObject) { |
| | | userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject; |
| | | } |
| | | |
| | | if (serializationError) { |
| | | userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError; |
| | | } |
| | | |
| | | dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ |
| | | if (self.completionHandler) { |
| | | self.completionHandler(task.response, responseObject, serializationError); |
| | | } |
| | | |
| | | dispatch_async(dispatch_get_main_queue(), ^{ |
| | | [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; |
| | | }); |
| | | }); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | #pragma mark - NSURLSessionDataDelegate |
| | | |
| | | - (void)URLSession:(__unused NSURLSession *)session |
| | | dataTask:(__unused NSURLSessionDataTask *)dataTask |
| | | didReceiveData:(NSData *)data |
| | | { |
| | | self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive; |
| | | self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived; |
| | | |
| | | [self.mutableData appendData:data]; |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task |
| | | didSendBodyData:(int64_t)bytesSent |
| | | totalBytesSent:(int64_t)totalBytesSent |
| | | totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{ |
| | | |
| | | self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend; |
| | | self.uploadProgress.completedUnitCount = task.countOfBytesSent; |
| | | } |
| | | |
| | | #pragma mark - NSURLSessionDownloadDelegate |
| | | |
| | | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask |
| | | didWriteData:(int64_t)bytesWritten |
| | | totalBytesWritten:(int64_t)totalBytesWritten |
| | | totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ |
| | | |
| | | self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite; |
| | | self.downloadProgress.completedUnitCount = totalBytesWritten; |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask |
| | | didResumeAtOffset:(int64_t)fileOffset |
| | | expectedTotalBytes:(int64_t)expectedTotalBytes{ |
| | | |
| | | self.downloadProgress.totalUnitCount = expectedTotalBytes; |
| | | self.downloadProgress.completedUnitCount = fileOffset; |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | downloadTask:(NSURLSessionDownloadTask *)downloadTask |
| | | didFinishDownloadingToURL:(NSURL *)location |
| | | { |
| | | self.downloadFileURL = nil; |
| | | |
| | | if (self.downloadTaskDidFinishDownloading) { |
| | | self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); |
| | | if (self.downloadFileURL) { |
| | | NSError *fileManagerError = nil; |
| | | |
| | | if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) { |
| | | [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | @end |
| | | |
| | | #pragma mark - |
| | | |
| | | /** |
| | | * A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`. |
| | | * |
| | | * See: |
| | | * - https://github.com/AFNetworking/AFNetworking/issues/1477 |
| | | * - https://github.com/AFNetworking/AFNetworking/issues/2638 |
| | | * - https://github.com/AFNetworking/AFNetworking/pull/2702 |
| | | */ |
| | | |
| | | static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) { |
| | | Method originalMethod = class_getInstanceMethod(theClass, originalSelector); |
| | | Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector); |
| | | method_exchangeImplementations(originalMethod, swizzledMethod); |
| | | } |
| | | |
| | | static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) { |
| | | return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method)); |
| | | } |
| | | |
| | | static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume"; |
| | | static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend"; |
| | | |
| | | @interface _AFURLSessionTaskSwizzling : NSObject |
| | | |
| | | @end |
| | | |
| | | @implementation _AFURLSessionTaskSwizzling |
| | | |
| | | + (void)load { |
| | | /** |
| | | WARNING: Trouble Ahead |
| | | https://github.com/AFNetworking/AFNetworking/pull/2702 |
| | | */ |
| | | |
| | | if (NSClassFromString(@"NSURLSessionTask")) { |
| | | /** |
| | | iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky. |
| | | Many Unit Tests have been built to validate as much of this behavior has possible. |
| | | Here is what we know: |
| | | - NSURLSessionTasks are implemented with class clusters, meaning the class you request from the API isn't actually the type of class you will get back. |
| | | - Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there. |
| | | - On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`. |
| | | - On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`. |
| | | - On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled. |
| | | - On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled. |
| | | - Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there. |
| | | |
| | | Some Assumptions: |
| | | - No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it. |
| | | - No background task classes override `resume` or `suspend` |
| | | |
| | | The current solution: |
| | | 1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task. |
| | | 2) Grab a pointer to the original implementation of `af_resume` |
| | | 3) Check to see if the current class has an implementation of resume. If so, continue to step 4. |
| | | 4) Grab the super class of the current class. |
| | | 5) Grab a pointer for the current class to the current implementation of `resume`. |
| | | 6) Grab a pointer for the super class to the current implementation of `resume`. |
| | | 7) If the current class implementation of `resume` is not equal to the super class implementation of `resume` AND the current implementation of `resume` is not equal to the original implementation of `af_resume`, THEN swizzle the methods |
| | | 8) Set the current class to the super class, and repeat steps 3-8 |
| | | */ |
| | | NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; |
| | | NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration]; |
| | | #pragma GCC diagnostic push |
| | | #pragma GCC diagnostic ignored "-Wnonnull" |
| | | NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil]; |
| | | #pragma clang diagnostic pop |
| | | IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume))); |
| | | Class currentClass = [localDataTask class]; |
| | | |
| | | while (class_getInstanceMethod(currentClass, @selector(resume))) { |
| | | Class superClass = [currentClass superclass]; |
| | | IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); |
| | | IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); |
| | | if (classResumeIMP != superclassResumeIMP && |
| | | originalAFResumeIMP != classResumeIMP) { |
| | | [self swizzleResumeAndSuspendMethodForClass:currentClass]; |
| | | } |
| | | currentClass = [currentClass superclass]; |
| | | } |
| | | |
| | | [localDataTask cancel]; |
| | | [session finishTasksAndInvalidate]; |
| | | } |
| | | } |
| | | |
| | | + (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass { |
| | | Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume)); |
| | | Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend)); |
| | | |
| | | if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) { |
| | | af_swizzleSelector(theClass, @selector(resume), @selector(af_resume)); |
| | | } |
| | | |
| | | if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) { |
| | | af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend)); |
| | | } |
| | | } |
| | | |
| | | - (NSURLSessionTaskState)state { |
| | | NSAssert(NO, @"State method should never be called in the actual dummy class"); |
| | | return NSURLSessionTaskStateCanceling; |
| | | } |
| | | |
| | | - (void)af_resume { |
| | | NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); |
| | | NSURLSessionTaskState state = [self state]; |
| | | [self af_resume]; |
| | | |
| | | if (state != NSURLSessionTaskStateRunning) { |
| | | [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; |
| | | } |
| | | } |
| | | |
| | | - (void)af_suspend { |
| | | NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); |
| | | NSURLSessionTaskState state = [self state]; |
| | | [self af_suspend]; |
| | | |
| | | if (state != NSURLSessionTaskStateSuspended) { |
| | | [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self]; |
| | | } |
| | | } |
| | | @end |
| | | |
| | | #pragma mark - |
| | | |
| | | @interface AFURLSessionManager () |
| | | @property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration; |
| | | @property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; |
| | | @property (readwrite, nonatomic, strong) NSURLSession *session; |
| | | @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; |
| | | @property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks; |
| | | @property (readwrite, nonatomic, strong) NSLock *lock; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos); |
| | | @property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; |
| | | @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume; |
| | | @end |
| | | |
| | | @implementation AFURLSessionManager |
| | | |
| | | - (instancetype)init { |
| | | return [self initWithSessionConfiguration:nil]; |
| | | } |
| | | |
| | | - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { |
| | | self = [super init]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | if (!configuration) { |
| | | configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; |
| | | } |
| | | |
| | | self.sessionConfiguration = configuration; |
| | | |
| | | self.operationQueue = [[NSOperationQueue alloc] init]; |
| | | self.operationQueue.maxConcurrentOperationCount = 1; |
| | | |
| | | self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; |
| | | |
| | | self.responseSerializer = [AFJSONResponseSerializer serializer]; |
| | | |
| | | self.securityPolicy = [AFSecurityPolicy defaultPolicy]; |
| | | |
| | | #if !TARGET_OS_WATCH |
| | | self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; |
| | | #endif |
| | | |
| | | self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; |
| | | |
| | | self.lock = [[NSLock alloc] init]; |
| | | self.lock.name = AFURLSessionManagerLockName; |
| | | |
| | | [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { |
| | | for (NSURLSessionDataTask *task in dataTasks) { |
| | | [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil]; |
| | | } |
| | | |
| | | for (NSURLSessionUploadTask *uploadTask in uploadTasks) { |
| | | [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil]; |
| | | } |
| | | |
| | | for (NSURLSessionDownloadTask *downloadTask in downloadTasks) { |
| | | [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil]; |
| | | } |
| | | }]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | - (void)dealloc { |
| | | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (NSString *)taskDescriptionForSessionTasks { |
| | | return [NSString stringWithFormat:@"%p", self]; |
| | | } |
| | | |
| | | - (void)taskDidResume:(NSNotification *)notification { |
| | | NSURLSessionTask *task = notification.object; |
| | | if ([task respondsToSelector:@selector(taskDescription)]) { |
| | | if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { |
| | | dispatch_async(dispatch_get_main_queue(), ^{ |
| | | [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task]; |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | |
| | | - (void)taskDidSuspend:(NSNotification *)notification { |
| | | NSURLSessionTask *task = notification.object; |
| | | if ([task respondsToSelector:@selector(taskDescription)]) { |
| | | if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { |
| | | dispatch_async(dispatch_get_main_queue(), ^{ |
| | | [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task]; |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task { |
| | | NSParameterAssert(task); |
| | | |
| | | AFURLSessionManagerTaskDelegate *delegate = nil; |
| | | [self.lock lock]; |
| | | delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)]; |
| | | [self.lock unlock]; |
| | | |
| | | return delegate; |
| | | } |
| | | |
| | | - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate |
| | | forTask:(NSURLSessionTask *)task |
| | | { |
| | | NSParameterAssert(task); |
| | | NSParameterAssert(delegate); |
| | | |
| | | [self.lock lock]; |
| | | self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; |
| | | [self addNotificationObserverForTask:task]; |
| | | [self.lock unlock]; |
| | | } |
| | | |
| | | - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask |
| | | uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock |
| | | downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock |
| | | completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler |
| | | { |
| | | AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask]; |
| | | delegate.manager = self; |
| | | delegate.completionHandler = completionHandler; |
| | | |
| | | dataTask.taskDescription = self.taskDescriptionForSessionTasks; |
| | | [self setDelegate:delegate forTask:dataTask]; |
| | | |
| | | delegate.uploadProgressBlock = uploadProgressBlock; |
| | | delegate.downloadProgressBlock = downloadProgressBlock; |
| | | } |
| | | |
| | | - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask |
| | | progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock |
| | | completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler |
| | | { |
| | | AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask]; |
| | | delegate.manager = self; |
| | | delegate.completionHandler = completionHandler; |
| | | |
| | | uploadTask.taskDescription = self.taskDescriptionForSessionTasks; |
| | | |
| | | [self setDelegate:delegate forTask:uploadTask]; |
| | | |
| | | delegate.uploadProgressBlock = uploadProgressBlock; |
| | | } |
| | | |
| | | - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask |
| | | progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock |
| | | destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination |
| | | completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler |
| | | { |
| | | AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask]; |
| | | delegate.manager = self; |
| | | delegate.completionHandler = completionHandler; |
| | | |
| | | if (destination) { |
| | | delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) { |
| | | return destination(location, task.response); |
| | | }; |
| | | } |
| | | |
| | | downloadTask.taskDescription = self.taskDescriptionForSessionTasks; |
| | | |
| | | [self setDelegate:delegate forTask:downloadTask]; |
| | | |
| | | delegate.downloadProgressBlock = downloadProgressBlock; |
| | | } |
| | | |
| | | - (void)removeDelegateForTask:(NSURLSessionTask *)task { |
| | | NSParameterAssert(task); |
| | | |
| | | [self.lock lock]; |
| | | [self removeNotificationObserverForTask:task]; |
| | | [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; |
| | | [self.lock unlock]; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (NSArray *)tasksForKeyPath:(NSString *)keyPath { |
| | | __block NSArray *tasks = nil; |
| | | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| | | [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { |
| | | if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) { |
| | | tasks = dataTasks; |
| | | } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) { |
| | | tasks = uploadTasks; |
| | | } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) { |
| | | tasks = downloadTasks; |
| | | } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) { |
| | | tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; |
| | | } |
| | | |
| | | dispatch_semaphore_signal(semaphore); |
| | | }]; |
| | | |
| | | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); |
| | | |
| | | return tasks; |
| | | } |
| | | |
| | | - (NSArray *)tasks { |
| | | return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; |
| | | } |
| | | |
| | | - (NSArray *)dataTasks { |
| | | return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; |
| | | } |
| | | |
| | | - (NSArray *)uploadTasks { |
| | | return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; |
| | | } |
| | | |
| | | - (NSArray *)downloadTasks { |
| | | return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks { |
| | | if (cancelPendingTasks) { |
| | | [self.session invalidateAndCancel]; |
| | | } else { |
| | | [self.session finishTasksAndInvalidate]; |
| | | } |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer { |
| | | NSParameterAssert(responseSerializer); |
| | | |
| | | _responseSerializer = responseSerializer; |
| | | } |
| | | |
| | | #pragma mark - |
| | | - (void)addNotificationObserverForTask:(NSURLSessionTask *)task { |
| | | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task]; |
| | | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task]; |
| | | } |
| | | |
| | | - (void)removeNotificationObserverForTask:(NSURLSessionTask *)task { |
| | | [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task]; |
| | | [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task]; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request |
| | | completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler |
| | | { |
| | | return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler]; |
| | | } |
| | | |
| | | - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request |
| | | uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock |
| | | downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock |
| | | completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler { |
| | | |
| | | __block NSURLSessionDataTask *dataTask = nil; |
| | | url_session_manager_create_task_safely(^{ |
| | | dataTask = [self.session dataTaskWithRequest:request]; |
| | | }); |
| | | |
| | | [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; |
| | | |
| | | return dataTask; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request |
| | | fromFile:(NSURL *)fileURL |
| | | progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock |
| | | completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler |
| | | { |
| | | __block NSURLSessionUploadTask *uploadTask = nil; |
| | | url_session_manager_create_task_safely(^{ |
| | | uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; |
| | | |
| | | // uploadTask may be nil on iOS7 because uploadTaskWithRequest:fromFile: may return nil despite being documented as nonnull (https://devforums.apple.com/message/926113#926113) |
| | | if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) { |
| | | for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) { |
| | | uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | if (uploadTask) { |
| | | [self addDelegateForUploadTask:uploadTask |
| | | progress:uploadProgressBlock |
| | | completionHandler:completionHandler]; |
| | | } |
| | | |
| | | return uploadTask; |
| | | } |
| | | |
| | | - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request |
| | | fromData:(NSData *)bodyData |
| | | progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock |
| | | completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler |
| | | { |
| | | __block NSURLSessionUploadTask *uploadTask = nil; |
| | | url_session_manager_create_task_safely(^{ |
| | | uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; |
| | | }); |
| | | |
| | | [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; |
| | | |
| | | return uploadTask; |
| | | } |
| | | |
| | | - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request |
| | | progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock |
| | | completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler |
| | | { |
| | | __block NSURLSessionUploadTask *uploadTask = nil; |
| | | url_session_manager_create_task_safely(^{ |
| | | uploadTask = [self.session uploadTaskWithStreamedRequest:request]; |
| | | }); |
| | | |
| | | [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; |
| | | |
| | | return uploadTask; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request |
| | | progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock |
| | | destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination |
| | | completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler |
| | | { |
| | | __block NSURLSessionDownloadTask *downloadTask = nil; |
| | | url_session_manager_create_task_safely(^{ |
| | | downloadTask = [self.session downloadTaskWithRequest:request]; |
| | | }); |
| | | |
| | | [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; |
| | | |
| | | return downloadTask; |
| | | } |
| | | |
| | | - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData |
| | | progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock |
| | | destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination |
| | | completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler |
| | | { |
| | | __block NSURLSessionDownloadTask *downloadTask = nil; |
| | | url_session_manager_create_task_safely(^{ |
| | | downloadTask = [self.session downloadTaskWithResumeData:resumeData]; |
| | | }); |
| | | |
| | | [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; |
| | | |
| | | return downloadTask; |
| | | } |
| | | |
| | | #pragma mark - |
| | | - (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task { |
| | | return [[self delegateForTask:task] uploadProgress]; |
| | | } |
| | | |
| | | - (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task { |
| | | return [[self delegateForTask:task] downloadProgress]; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block { |
| | | self.sessionDidBecomeInvalid = block; |
| | | } |
| | | |
| | | - (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { |
| | | self.sessionDidReceiveAuthenticationChallenge = block; |
| | | } |
| | | |
| | | #if !TARGET_OS_OSX |
| | | - (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block { |
| | | self.didFinishEventsForBackgroundURLSession = block; |
| | | } |
| | | #endif |
| | | |
| | | #pragma mark - |
| | | |
| | | - (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block { |
| | | self.taskNeedNewBodyStream = block; |
| | | } |
| | | |
| | | - (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block { |
| | | self.taskWillPerformHTTPRedirection = block; |
| | | } |
| | | |
| | | - (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { |
| | | self.taskDidReceiveAuthenticationChallenge = block; |
| | | } |
| | | |
| | | - (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block { |
| | | self.taskDidSendBodyData = block; |
| | | } |
| | | |
| | | - (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block { |
| | | self.taskDidComplete = block; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block { |
| | | self.dataTaskDidReceiveResponse = block; |
| | | } |
| | | |
| | | - (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block { |
| | | self.dataTaskDidBecomeDownloadTask = block; |
| | | } |
| | | |
| | | - (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block { |
| | | self.dataTaskDidReceiveData = block; |
| | | } |
| | | |
| | | - (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block { |
| | | self.dataTaskWillCacheResponse = block; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block { |
| | | self.downloadTaskDidFinishDownloading = block; |
| | | } |
| | | |
| | | - (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block { |
| | | self.downloadTaskDidWriteData = block; |
| | | } |
| | | |
| | | - (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block { |
| | | self.downloadTaskDidResume = block; |
| | | } |
| | | |
| | | #pragma mark - NSObject |
| | | |
| | | - (NSString *)description { |
| | | return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue]; |
| | | } |
| | | |
| | | - (BOOL)respondsToSelector:(SEL)selector { |
| | | if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) { |
| | | return self.taskWillPerformHTTPRedirection != nil; |
| | | } else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) { |
| | | return self.dataTaskDidReceiveResponse != nil; |
| | | } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) { |
| | | return self.dataTaskWillCacheResponse != nil; |
| | | } |
| | | #if !TARGET_OS_OSX |
| | | else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) { |
| | | return self.didFinishEventsForBackgroundURLSession != nil; |
| | | } |
| | | #endif |
| | | |
| | | return [[self class] instancesRespondToSelector:selector]; |
| | | } |
| | | |
| | | #pragma mark - NSURLSessionDelegate |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | didBecomeInvalidWithError:(NSError *)error |
| | | { |
| | | if (self.sessionDidBecomeInvalid) { |
| | | self.sessionDidBecomeInvalid(session, error); |
| | | } |
| | | |
| | | [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge |
| | | completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler |
| | | { |
| | | NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; |
| | | __block NSURLCredential *credential = nil; |
| | | |
| | | if (self.sessionDidReceiveAuthenticationChallenge) { |
| | | disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); |
| | | } else { |
| | | if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { |
| | | if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { |
| | | credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; |
| | | if (credential) { |
| | | disposition = NSURLSessionAuthChallengeUseCredential; |
| | | } else { |
| | | disposition = NSURLSessionAuthChallengePerformDefaultHandling; |
| | | } |
| | | } else { |
| | | disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; |
| | | } |
| | | } else { |
| | | disposition = NSURLSessionAuthChallengePerformDefaultHandling; |
| | | } |
| | | } |
| | | |
| | | if (completionHandler) { |
| | | completionHandler(disposition, credential); |
| | | } |
| | | } |
| | | |
| | | #pragma mark - NSURLSessionTaskDelegate |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | task:(NSURLSessionTask *)task |
| | | willPerformHTTPRedirection:(NSHTTPURLResponse *)response |
| | | newRequest:(NSURLRequest *)request |
| | | completionHandler:(void (^)(NSURLRequest *))completionHandler |
| | | { |
| | | NSURLRequest *redirectRequest = request; |
| | | |
| | | if (self.taskWillPerformHTTPRedirection) { |
| | | redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); |
| | | } |
| | | |
| | | if (completionHandler) { |
| | | completionHandler(redirectRequest); |
| | | } |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | task:(NSURLSessionTask *)task |
| | | didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge |
| | | completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler |
| | | { |
| | | NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; |
| | | __block NSURLCredential *credential = nil; |
| | | |
| | | if (self.taskDidReceiveAuthenticationChallenge) { |
| | | disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential); |
| | | } else { |
| | | if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { |
| | | if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { |
| | | disposition = NSURLSessionAuthChallengeUseCredential; |
| | | credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; |
| | | } else { |
| | | disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; |
| | | } |
| | | } else { |
| | | disposition = NSURLSessionAuthChallengePerformDefaultHandling; |
| | | } |
| | | } |
| | | |
| | | if (completionHandler) { |
| | | completionHandler(disposition, credential); |
| | | } |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | task:(NSURLSessionTask *)task |
| | | needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler |
| | | { |
| | | NSInputStream *inputStream = nil; |
| | | |
| | | if (self.taskNeedNewBodyStream) { |
| | | inputStream = self.taskNeedNewBodyStream(session, task); |
| | | } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { |
| | | inputStream = [task.originalRequest.HTTPBodyStream copy]; |
| | | } |
| | | |
| | | if (completionHandler) { |
| | | completionHandler(inputStream); |
| | | } |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | task:(NSURLSessionTask *)task |
| | | didSendBodyData:(int64_t)bytesSent |
| | | totalBytesSent:(int64_t)totalBytesSent |
| | | totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend |
| | | { |
| | | |
| | | int64_t totalUnitCount = totalBytesExpectedToSend; |
| | | if(totalUnitCount == NSURLSessionTransferSizeUnknown) { |
| | | NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; |
| | | if(contentLength) { |
| | | totalUnitCount = (int64_t) [contentLength longLongValue]; |
| | | } |
| | | } |
| | | |
| | | AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; |
| | | |
| | | if (delegate) { |
| | | [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend]; |
| | | } |
| | | |
| | | if (self.taskDidSendBodyData) { |
| | | self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount); |
| | | } |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | task:(NSURLSessionTask *)task |
| | | didCompleteWithError:(NSError *)error |
| | | { |
| | | AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; |
| | | |
| | | // delegate may be nil when completing a task in the background |
| | | if (delegate) { |
| | | [delegate URLSession:session task:task didCompleteWithError:error]; |
| | | |
| | | [self removeDelegateForTask:task]; |
| | | } |
| | | |
| | | if (self.taskDidComplete) { |
| | | self.taskDidComplete(session, task, error); |
| | | } |
| | | } |
| | | |
| | | #pragma mark - NSURLSessionDataDelegate |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | dataTask:(NSURLSessionDataTask *)dataTask |
| | | didReceiveResponse:(NSURLResponse *)response |
| | | completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler |
| | | { |
| | | NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; |
| | | |
| | | if (self.dataTaskDidReceiveResponse) { |
| | | disposition = self.dataTaskDidReceiveResponse(session, dataTask, response); |
| | | } |
| | | |
| | | if (completionHandler) { |
| | | completionHandler(disposition); |
| | | } |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | dataTask:(NSURLSessionDataTask *)dataTask |
| | | didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask |
| | | { |
| | | AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; |
| | | if (delegate) { |
| | | [self removeDelegateForTask:dataTask]; |
| | | [self setDelegate:delegate forTask:downloadTask]; |
| | | } |
| | | |
| | | if (self.dataTaskDidBecomeDownloadTask) { |
| | | self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask); |
| | | } |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | dataTask:(NSURLSessionDataTask *)dataTask |
| | | didReceiveData:(NSData *)data |
| | | { |
| | | |
| | | AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; |
| | | [delegate URLSession:session dataTask:dataTask didReceiveData:data]; |
| | | |
| | | if (self.dataTaskDidReceiveData) { |
| | | self.dataTaskDidReceiveData(session, dataTask, data); |
| | | } |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | dataTask:(NSURLSessionDataTask *)dataTask |
| | | willCacheResponse:(NSCachedURLResponse *)proposedResponse |
| | | completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler |
| | | { |
| | | NSCachedURLResponse *cachedResponse = proposedResponse; |
| | | |
| | | if (self.dataTaskWillCacheResponse) { |
| | | cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse); |
| | | } |
| | | |
| | | if (completionHandler) { |
| | | completionHandler(cachedResponse); |
| | | } |
| | | } |
| | | |
| | | #if !TARGET_OS_OSX |
| | | - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { |
| | | if (self.didFinishEventsForBackgroundURLSession) { |
| | | dispatch_async(dispatch_get_main_queue(), ^{ |
| | | self.didFinishEventsForBackgroundURLSession(session); |
| | | }); |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | #pragma mark - NSURLSessionDownloadDelegate |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | downloadTask:(NSURLSessionDownloadTask *)downloadTask |
| | | didFinishDownloadingToURL:(NSURL *)location |
| | | { |
| | | AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; |
| | | if (self.downloadTaskDidFinishDownloading) { |
| | | NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); |
| | | if (fileURL) { |
| | | delegate.downloadFileURL = fileURL; |
| | | NSError *error = nil; |
| | | |
| | | if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) { |
| | | [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; |
| | | } |
| | | |
| | | return; |
| | | } |
| | | } |
| | | |
| | | if (delegate) { |
| | | [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; |
| | | } |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | downloadTask:(NSURLSessionDownloadTask *)downloadTask |
| | | didWriteData:(int64_t)bytesWritten |
| | | totalBytesWritten:(int64_t)totalBytesWritten |
| | | totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite |
| | | { |
| | | |
| | | AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; |
| | | |
| | | if (delegate) { |
| | | [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; |
| | | } |
| | | |
| | | if (self.downloadTaskDidWriteData) { |
| | | self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); |
| | | } |
| | | } |
| | | |
| | | - (void)URLSession:(NSURLSession *)session |
| | | downloadTask:(NSURLSessionDownloadTask *)downloadTask |
| | | didResumeAtOffset:(int64_t)fileOffset |
| | | expectedTotalBytes:(int64_t)expectedTotalBytes |
| | | { |
| | | |
| | | AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; |
| | | |
| | | if (delegate) { |
| | | [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes]; |
| | | } |
| | | |
| | | if (self.downloadTaskDidResume) { |
| | | self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); |
| | | } |
| | | } |
| | | |
| | | #pragma mark - NSSecureCoding |
| | | |
| | | + (BOOL)supportsSecureCoding { |
| | | return YES; |
| | | } |
| | | |
| | | - (instancetype)initWithCoder:(NSCoder *)decoder { |
| | | NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; |
| | | |
| | | self = [self initWithSessionConfiguration:configuration]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | return self; |
| | | } |
| | | |
| | | - (void)encodeWithCoder:(NSCoder *)coder { |
| | | [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; |
| | | } |
| | | |
| | | #pragma mark - NSCopying |
| | | |
| | | - (instancetype)copyWithZone:(NSZone *)zone { |
| | | return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration]; |
| | | } |
| | | |
| | | @end |