NSURLSession, NSURLSessionUploadTask, NSURLSessionDownload task delegates not firing. NSURLSession in one class (Singleton), NSURLUploadTask and NSURLDownloadTask and related delegates in sub classes of NSOperation

I'm in the process of creating an upload/download media feature for my app. I had the queue working fine for uploads but when I added downloads and the requisite download delegates the upload delegate was called event though the task was to download. My initial class structure was a singleton QueueController that had the NSURLSession and delegates (but not download) along with a TransferModel that could hold either the upload or download. When I tried to add downloads the callbacks were not working right so I moved to put the transfer related delegates in two sub classes TransferUploadModel and TransferDownloadModel but now my delegates are not firing.

Here is what my method signatures look like: QueueController:


@interface QueueController : NSObject<NSURLSessionDelegate>



@property(nonatomic, weak) NSObject<QueueControllerDelegate>* delegate;

@property(atomic, strong) NSURLSession* session;

@property(nonatomic) NSURLSessionConfiguration* configuration;



+ (QueueController*)sharedInstance;



@end



@implementation QueueController {

- (void)application:(UIApplication *)application

handleEventsForBackgroundURLSession:(NSString *)identifier

completionHandler:(void (^)(void))completionHandler {

//...

}



TransferUploadModel:


@interface TransferUploadModel : TransferModel <NSURLSessionTaskDelegate,

NSURLSessionDataDelegate>

//...



@end





//Note TransferModel is a subclass of NSOperation

@implementation TransferUploadModel





- (id)initWithMedia:(MediaItem*)mediaItem_

withTransferType:(TransferType)transferType_

andWiths3Path:s3Path_

andWiths3file_name:s3file_name_

andWithNSURLSession:session {

}





- (void)main {

//NSOperation override

}







//

// Transfer upload started

//

- (void)uploadMedia {

/*

* Fetch signed URL

*/

AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];

getPreSignedURLRequest.bucket = BUCKET_NAME;

getPreSignedURLRequest.key = @"mypic.jpeg";

getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodPUT;

getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:3600];


// Important: must set contentType for PUT request

getPreSignedURLRequest.contentType = self.content_type;

NSLog(@"headers: %@", getPreSignedURLRequest);


/*

* Upload the file

*/

[[[AWSS3PreSignedURLBuilder defaultS3PreSignedURLBuilder] getPreSignedURL:getPreSignedURLRequest] continueWithBlock:^id(AWSTask *task) {


if (task.error) {

NSLog(@"Error: %@", task.error);

} else {

NSURL* presignedURL = task.result;

NSLog(@"upload presignedURL is: \n%@", presignedURL);


NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:presignedURL];

request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

[request setHTTPMethod:@"PUT"];

[request setValue:self.content_type forHTTPHeaderField:@"Content-Type"];


@try {

self.nsurlSessionUploadTask = [self.nsurlSession uploadTaskWithRequest:request

fromFile:self.mediaCopyURL];

[self.nsurlSessionUploadTask resume];

//

// Delegates don't fire after this...

//

} @catch (NSException* exception) {

NSLog(@"exception creating upload task: %@", exception);

}

}

return nil;

}];

}





//

// NSURLSessionDataDelegate : didReceiveData

//

- (void)URLSession:(NSURLSession *)session

dataTask:(NSURLSessionDataTask *)dataTask

didReceiveData:(NSData *)data {

NSLog(@"...");

}



//

// Initial response from server with headers

//

- (void)URLSession:(NSURLSession *)session

dataTask:(NSURLSessionDataTask *)dataTask

didReceiveResponse:(NSURLResponse *)response

completionHandler:

(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {

NSLog(@"response.description:%@", response.description);

completionHandler(NSURLSessionResponseAllow);

}



//

// Upload transfer in progress

//

- (void)URLSession:(NSURLSession *)session

task:(NSURLSessionUploadTask *)task

didSendBodyData:(int64_t)bytesSent

totalBytesSent:(int64_t)totalBytesSent

totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {

@try {

NSLog(@"...");

} @catch (NSException *exception) {

NSLog(@"%s exception: %@", __PRETTY_FUNCTION__, exception);

}

}



//

// Upload transfer completed

//

- (void)URLSession:(NSURLSession *)session

task:(NSURLSessionTask *)task

didCompleteWithError:(NSError *)error {

NSLog(@"...");

} // end URLSession:session:task:error





@end



Any help is much appreciated.



Thanks,

J

Ok, I found that if I moved the NSURLSession into the TransferUploadModel and TransferDownloadModel then the delegates get called. Since these models are many in a queue I init as so:


@implementation TransferUploadModel

static NSURLSession *session = nil;

static dispatch_once_t onceToken;

- (id) init {

self = [super init];

dispatch_once(&onceToken, ^{

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"transferUploadQueue"];

configuration.sessionSendsLaunchEvents = YES;

configuration.discretionary = YES;

session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];

});

self.session = session;

}

//...

@end


Thanks,

John

I recommend against starting a session per request. Read see this thread for more information.

There are at least three ways to resolve this:

  • Use the NSURLSession task creation methods that implement a completion handler block.

  • Have your QueueController class accept raw delegate events, handle all the common work, and then pass the request-specific stuff up to your TransferModel subclass via its own delegate protocol.

  • Use something like the QNSURLSessionDemux class (which you’ll find in the CustomHTTPProtocol sample code) to do a full demultiplex.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
NSURLSession, NSURLSessionUploadTask, NSURLSessionDownload task delegates not firing. NSURLSession in one class (Singleton), NSURLUploadTask and NSURLDownloadTask and related delegates in sub classes of NSOperation
 
 
Q