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.
Came here to understand how to mock URLSession
tasks such as URLSessionDataTask
?
Circa Swift 5, it is much easier to mock the URLProtocol
that URLSession
is using to send the request.
See Unit Testing URLSession using URLProtocol.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With