-
Notifications
You must be signed in to change notification settings - Fork 47
Working with URLSession
There are many considerations for the networking stack in your application. Given the breadth of these considerations, we didn't want a one-size-fits-all route for doing networking with Deferred. For instance, using URLSessionDelegate
disables the convenience callback methods like URLSession.dataTask(with:completionHandler:)
that would typically be used by a solution built-in to Deferred.
With that caveat, the Task
module to Deferred provides a great way to tie it all together.
Beginning in iOS 11, macOS High Sierra, watchOS 4, and tvOS 11, URLSession
has built-in progress handling that is compatible with Task
.
extension URLSession {
/// Returns the data from the contents of a URL, based on `request`, as a
/// future.
///
/// The session bypasses delegate methods for result delivery. Delegate
/// methods for handling authentication challenges are still called.
///
/// - parameter request: Object that provides the URL, body data, and so on.
/// - parameter configure: An optional callback for setting properties on
/// the URL task.
func beginDataTask(with request: URLRequest) -> Task<(Data, URLResponse)> {
let deferred = Deferred<Task<(Data, URLResponse)>.Result>()
let task = dataTask(with: request) { (data, response, error) in
if let error = error {
deferred.fail(with: error)
} else if let data = data, let response = response {
deferred.succeed(with: (data, response))
} else {
deferred.fail(with: URLError.badServerResponse)
}
}
task.resume()
return Task(deferred, progress: task.progress)
}
}
When maintaining your own session client, it is vital to learn that URLSession
instances are self-contained, and all tasks within a session are unique by their taskIdentifier
. This is useful for tracking constituent tasks. Consider the pseudocode below:
final class URLClient: NSObject, URLSessionTaskDelegate {
private final class TaskState: NSObject {
...
}
private let urlSession: URLSession
private var tasks = [Int: TaskState]()
func URLSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
tasks.removeAll()
}
func URLSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard let state = tasks.removeValue(forKey: task.taskIdentifier) else { return }
// use `state`
}
func URLSession(_ session: URLSession, dataTask task: URLSessionDataTask, didReceive response: URLResponse, completionHandler: URLSessionResponseDisposition -> Void) {
state[task.taskIdentifier] = TaskState(...)
completionHandler(.allow)
}
}
© 2014-2018 under MIT. Brought to you by your friends at Big Nerd Ranch.