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