Source/TNLBackgroundURLSessionTaskOperationManager.m (118 lines of code) (raw):

// // TNLBackgroundURLSessionTaskOperationManager.m // TwitterNetworkLayer // // Created on 8/6/14. // Copyright © 2020 Twitter. All rights reserved. // #import <mach/mach_time.h> #import "NSURLSessionConfiguration+TNLAdditions.h" #import "TNL_Project.h" #import "TNLBackgroundURLSessionTaskOperationManager.h" #import "TNLRequest.h" #import "TNLRequestConfiguration_Project.h" #import "TNLResponse.h" #import "TNLTemporaryFile_Project.h" #import "TNLTiming.h" #import "TNLURLSessionManager.h" NS_ASSUME_NONNULL_BEGIN TNL_OBJC_FINAL TNL_OBJC_DIRECT_MEMBERS @interface TNLBackgroundRequestContext : NSObject @property (nonatomic, nullable) NSURLSessionTask *URLSessionTask; @property (nonatomic, nullable) NSHTTPURLResponse *URLResponse; @property (nonatomic, nullable) NSError *error; @property (nonatomic, nullable) TNLTemporaryFile *tempFile; @end TNL_OBJC_DIRECT_MEMBERS @interface TNLBackgroundURLSessionTaskOperationManager () <NSURLSessionDownloadDelegate, NSURLSessionDataDelegate> - (nullable TNLBackgroundRequestContext *)_backgroundRequestContextForTask:(NSURLSessionTask *)task createIfNecessary:(BOOL)createIfNecessary; @end @implementation TNLBackgroundURLSessionTaskOperationManager { NSURLSession *_URLSession; NSMutableDictionary *_contexts; } - (instancetype)init { self = [super init]; if (self) { _contexts = [[NSMutableDictionary alloc] init]; } return self; } - (void)handleBackgroundURLSessionEvents:(NSString *)identifier { // TODO: change this so that the URLSession is owned by TNLURLSessionManager NSURLSessionConfiguration *config = [NSURLSessionConfiguration tnl_backgroundSessionConfigurationWithTaggedIdentifier:identifier]; _URLSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]]; } - (nullable TNLBackgroundRequestContext *)_backgroundRequestContextForTask:(NSURLSessionTask *)task createIfNecessary:(BOOL)createIfNecessary { NSNumber *taskIdNumber = @(task.taskIdentifier); TNLBackgroundRequestContext *context = _contexts[taskIdNumber]; if (!context && createIfNecessary) { context = [[TNLBackgroundRequestContext alloc] init]; context.URLSessionTask = task; _contexts[taskIdNumber] = context; } return context; } #pragma mark NSURLSessionDelegate #if TARGET_OS_IPHONE // == IOS + WATCH + TV - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { TNLAssert(nil != session.configuration.identifier); [[TNLURLSessionManager sharedInstance] URLSessionDidCompleteBackgroundEvents:session]; } #endif - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error { TNLBackgroundRequestContext *context = [self _backgroundRequestContextForTask:task createIfNecessary:YES]; TNLAssert(context != nil); TNLAssert(context.URLSessionTask.taskIdentifier == task.taskIdentifier); if (error && !context.error) { context.error = error; } if ([task.response isKindOfClass:[NSHTTPURLResponse class]]) { context.URLResponse = (id)task.response; } NSString *sharedContainerIdentifier = _URLSession.configuration.sharedContainerIdentifier; TNLResponseInfo *info = [[TNLResponseInfo alloc] initWithFinalURLRequest:task.currentRequest URLResponse:context.URLResponse source:TNLResponseSourceNetworkRequest data:nil temporarySavedFile:context.tempFile]; TNLResponseMetrics *metrics = [[TNLResponseMetrics alloc] initWithEnqueueDate:[NSDate date] enqueueTime:mach_absolute_time() completeDate:[NSDate date] completeTime:mach_absolute_time() attemptMetrics:nil]; TNLResponse *response = [TNLResponse responseWithRequest:task.currentRequest operationError:context.error info:info metrics:metrics]; [[TNLURLSessionManager sharedInstance] URLSessionDidCompleteBackgroundTask:task.taskIdentifier sessionConfigIdentifier:_URLSession.configuration.identifier sharedContainerIdentifier:sharedContainerIdentifier request:task.originalRequest response:response]; } #pragma mark NSURLSessionDownloadTaskDelegate - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { TNLBackgroundRequestContext *context = [self _backgroundRequestContextForTask:downloadTask createIfNecessary:YES]; TNLAssert(context != nil); TNLAssert(context.URLSessionTask.taskIdentifier == downloadTask.taskIdentifier); // Capture the temp file immediately NSError *error; context.tempFile = [TNLTemporaryFile temporaryFileWithExistingFilePath:location.path error:&error]; if (!context.tempFile && !context.error) { context.error = error; } } @end @implementation TNLBackgroundRequestContext @end NS_ASSUME_NONNULL_END