I have been following this tutorial to stub out URLSession
. The example was done by creating a protocol and extending the existing URLSession
.
protocol URLSessionProtocol {
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
func dataTask(with request: NSURLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
}
extension URLSession: URLSessionProtocol {
func dataTask(with request: NSURLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol {
return dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTaskProtocol
}
}
The unit tests work as expected. But when I try to run the real thing, the URLSession -> datatask() gets into an infinite loop and it crashes. It seems to be that datatask() is calling itself.
What am I overlooking, please?
UPDATE:
protocol URLSessionDataTaskProtocol {
var originalRequest: URLRequest? { get }
func resume()
}
extension URLSessionDataTask: URLSessionDataTaskProtocol {}
I have finally found the solution. It’s fascinating as we missed the wood for the trees. There are two issues:
1) It seems that Swift 4 has changed the signature for dataTask(with: NSURLRequest)
to dataTask(with: URLRequest)
Therefore the line in my opening question would only match to the Protocol's func signature, and it would never hit the dataTask
inside URLSession
, hence the infinite loop. To solve this issue I had to change NSURLRequest
to URLRequest
and refactor the code accordingly.
2) The signature remains vague, hence it is better to store the result as dataTask first with a cast to URLSessionDataTask
and then return the variable.
New refactored Code for Swift 4:
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
protocol URLSessionProtocol {
func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
}
extension URLSession: URLSessionProtocol {
func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol {
let task:URLSessionDataTask = dataTask(with: request, completionHandler: {
(data:Data?, response:URLResponse?, error:Error?) in completionHandler(data,response,error) }) as URLSessionDataTask
return task
}
}
I also found I had to inject URLSession.shared
as a singleton and not as URLSession()
, otherwise it could crash.