Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSURLSessionTaskDelegate method URLSession:task:didCompleteWithError: never called

All of my networking code relies on the NSURLSession delegate methods--rather than completion handlers. My data & download tasks all work great, but my upload tasks never result in URLSession:task:didCompleteWithError: being called. However, the URLSession:dataTask:didReceiveData: and URLSession:dataTask:willCacheResponse:completionHandler: delegate methods ARE called.

If I set the resource timeout to a really low value on my session object, then didCompleteWithErrors does get called, but that's clearly not a solution.

Any ideas? I'm about to go insane here.

Thanks.

like image 745
mph Avatar asked Nov 17 '14 23:11

mph


4 Answers

You will not see didCompleteWithError called if you implement willCacheResponse, but if the implementation fails to actually call the completionHandler. You must call the completionHandler.

The same is true with any and all of the various NSURLSession delegate methods that provide a completionHandler parameter (e.g. authentication challenges, redirects, etc.). If you implement these respective methods, you must ensure that the completionHandler is called.

like image 79
Rob Avatar answered Nov 19 '22 20:11

Rob


Another possibility someone may want to check in this case, is that your URLSession delegate methods are not marked private. If they are, the methods will not be called... this can be easy to do by accident if you leave modifiers off the methods, then use auto-fix on a warning to fix the modifiers for the method (makes it private).

Method signature should be:

public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) 
like image 29
Kendall Helmstetter Gelner Avatar answered Nov 19 '22 19:11

Kendall Helmstetter Gelner


Rob's answer is correct and gave me a big clue. However - for my specific problem - it didn't jump out at me why the NSURLSessionTaskDelegate methods might not be called. If I could be allowed to provide another answer with a different focus.

Many of the NSURLSession task creation methods have two variations; one with a completionHandler parameter and one without.

For example:

func dataTaskWithRequest(_ request: NSURLRequest) -> NSURLSessionDataTask

func dataTaskWithRequest(_ request: NSURLRequest, completionHandler completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask

If the completionHandler method is used, then any delegate methods - in this case for NSURLSessionTaskDelegate will be ignored. That's despite the fact that a delegate has been specified in the NSURLSession initialiser.

The following Playground code demonstrates this. Toggle the task declaration comments to see the delegate called or not:

import UIKit
import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true


class MyDelegate:NSObject, NSURLSessionTaskDelegate
{
    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
        NSLog("Delegate called")
    }
}


let myDel = MyDelegate()
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: myDel, delegateQueue: NSOperationQueue())
let url = NSURL(string: "http://httpbin.org")
let request: NSURLRequest = NSURLRequest(URL: url!)

// With completionHandler, delegate method above NOT called
let task : NSURLSessionDataTask = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
        NSLog("Task done")
})

// Without completionHandler, delegate method above IS called
//let task : NSURLSessionDataTask = session.dataTaskWithRequest(request)

task.resume()

TLDR: tearing your hair out wondering why the NSURLSession's delegate methods aren't called? User the alternative method signature to create your session tasks; that which omits the completionHandler parameter.

like image 2
Max MacLeod Avatar answered Nov 19 '22 20:11

Max MacLeod


To expand upon another answer here, the NSURLSession docs state that either the completion handler or delegate methods will be called.

Like most networking APIs, the NSURLSession API is highly asynchronous. It returns data to your app in one of two ways, depending on the methods you call:

  • To a completion handler block that is called when a transfer finishes successfully or with an error.

  • By calling methods on the session’s delegate as data is received and when the transfer is complete.

A more specific ask of this question is here:

NSURLSession delegate and completionHandler

like image 1
pkamb Avatar answered Nov 19 '22 19:11

pkamb