[REQ] [Swift] Abstract away URLSession
Created by: Czajnikowski
Is your feature request related to a problem? Please describe.
Yes. I'm trying to implement a caching mechanism that basically overrides the URLSession.dataTask
and under the hood it takes the response from the cache (if available) and at the same time loads using the URLSession
itself. Currently, the only way of changing the URLSession
is via override of URLSessionRequestBuilder.createURLSession
(well, I can always write my own RequestBuilder
, but that'd be a lot of duplicated code). So to be able to implement my approach I'd have to subclass URLSession
and override the dataTask
.
The implementation would look like this:
class CacheFirstURLSession: URLSession {
override func dataTask(
with request: URLRequest,
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
) -> URLSessionDataTask {
if let cachedResponse = configuration.urlCache?.cachedResponse(for: request) {
completionHandler(cachedResponse.data, cachedResponse.response, nil)
}
return super.dataTask(with: request, completionHandler: completionHandler)
}
}
This is not currently doable (as far as I know, URLSession
s are not subclassable). I tried a couple of times to make a subclass, but it does not work - calling dataTask
crashes the app. Probably because there's no way to inject configuration into a subclass.
Describe the solution you'd like
So I don't necessarily need a subclass of the URLSession
, I can go with a wrapper like :
class URLSessionWrapper {
private let wrapped: URLSession
init(with wrapped: URLSession) {
self.wrapped = wrapped
}
}
extension URLSessionWrapper: DataTaskMaking {
func dataTask(
with request: URLRequest,
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
) -> URLSessionDataTask {
if let cachedResponse = wrapped.configuration.urlCache?.cachedResponse(for: request) {
completionHandler(cachedResponse.data, cachedResponse.response, nil)
}
return wrapped.dataTask(with: request, completionHandler: completionHandler)
}
}
The only problem that I have now is that the URLSessionRequestBuilder.createURLSession
returns the exact URLSession
. If we'd be able to exchange it into a protocol then my problem is solved.
Here's an initial idea of what we'd have to do in the URLSessionImplementation.mustache
:
Somewhere at the top (or bottom):
public protocol DataTaskMaking {
func dataTask(
with request: URLRequest,
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
) -> URLSessionDataTask
}
extension URLSession: DataTaskMaking {}
And then change the override:
{...} func createURLSession() -> DataTaskMaking {
return defaultURLSession
}
Describe alternatives you've considered
So I tried solving the problem with subclassing and extensions which both don't seem to be feasible in our current architecture. Another possible option is to create my own own RequestBuilder
, but that'd be a lot of duplicated code.
Additional context
Nope