2 // SessionDelegate.swift
4 // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 /// Responsible for handling all delegate callbacks for the underlying session.
28 open class SessionDelegate: NSObject {
30 // MARK: URLSessionDelegate Overrides
32 /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
33 open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
35 /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
36 open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
38 /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
39 open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
41 /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`.
42 open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?
44 // MARK: URLSessionTaskDelegate Overrides
46 /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`.
47 open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
49 /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and
50 /// requires the caller to call the `completionHandler`.
51 open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)?
53 /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`.
54 open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
56 /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and
57 /// requires the caller to call the `completionHandler`.
58 open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
60 /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`.
61 open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
63 /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and
64 /// requires the caller to call the `completionHandler`.
65 open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)?
67 /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`.
68 open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
70 /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`.
71 open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
73 // MARK: URLSessionDataDelegate Overrides
75 /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`.
76 open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
78 /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and
79 /// requires caller to call the `completionHandler`.
80 open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)?
82 /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`.
83 open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
85 /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`.
86 open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
88 /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`.
89 open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
91 /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and
92 /// requires caller to call the `completionHandler`.
93 open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)?
95 // MARK: URLSessionDownloadDelegate Overrides
97 /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`.
98 open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?
100 /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`.
101 open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
103 /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`.
104 open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
106 // MARK: URLSessionStreamDelegate Overrides
110 /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`.
111 @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
112 open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
114 return _streamTaskReadClosed as? (URLSession, URLSessionStreamTask) -> Void
117 _streamTaskReadClosed = newValue
121 /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`.
122 @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
123 open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
125 return _streamTaskWriteClosed as? (URLSession, URLSessionStreamTask) -> Void
128 _streamTaskWriteClosed = newValue
132 /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`.
133 @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
134 open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)? {
136 return _streamTaskBetterRouteDiscovered as? (URLSession, URLSessionStreamTask) -> Void
139 _streamTaskBetterRouteDiscovered = newValue
143 /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`.
144 @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
145 open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)? {
147 return _streamTaskDidBecomeInputStream as? (URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void
150 _streamTaskDidBecomeInputStream = newValue
154 var _streamTaskReadClosed: Any?
155 var _streamTaskWriteClosed: Any?
156 var _streamTaskBetterRouteDiscovered: Any?
157 var _streamTaskDidBecomeInputStream: Any?
163 var retrier: RequestRetrier?
164 weak var sessionManager: SessionManager?
166 private var requests: [Int: Request] = [:]
167 private let lock = NSLock()
169 /// Access the task delegate for the specified task in a thread-safe manner.
170 open subscript(task: URLSessionTask) -> Request? {
172 lock.lock() ; defer { lock.unlock() }
173 return requests[task.taskIdentifier]
176 lock.lock() ; defer { lock.unlock() }
177 requests[task.taskIdentifier] = newValue
183 /// Initializes the `SessionDelegate` instance.
185 /// - returns: The new `SessionDelegate` instance.
186 public override init() {
190 // MARK: NSObject Overrides
192 /// Returns a `Bool` indicating whether the `SessionDelegate` implements or inherits a method that can respond
193 /// to a specified message.
195 /// - parameter selector: A selector that identifies a message.
197 /// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`.
198 open override func responds(to selector: Selector) -> Bool {
200 if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
201 return sessionDidFinishEventsForBackgroundURLSession != nil
206 if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) {
208 case #selector(URLSessionStreamDelegate.urlSession(_:readClosedFor:)):
209 return streamTaskReadClosed != nil
210 case #selector(URLSessionStreamDelegate.urlSession(_:writeClosedFor:)):
211 return streamTaskWriteClosed != nil
212 case #selector(URLSessionStreamDelegate.urlSession(_:betterRouteDiscoveredFor:)):
213 return streamTaskBetterRouteDiscovered != nil
214 case #selector(URLSessionStreamDelegate.urlSession(_:streamTask:didBecome:outputStream:)):
215 return streamTaskDidBecomeInputAndOutputStreams != nil
223 case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)):
224 return sessionDidBecomeInvalidWithError != nil
225 case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)):
226 return (sessionDidReceiveChallenge != nil || sessionDidReceiveChallengeWithCompletion != nil)
227 case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
228 return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
229 case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)):
230 return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
232 return type(of: self).instancesRespond(to: selector)
237 // MARK: - URLSessionDelegate
239 extension SessionDelegate: URLSessionDelegate {
240 /// Tells the delegate that the session has been invalidated.
242 /// - parameter session: The session object that was invalidated.
243 /// - parameter error: The error that caused invalidation, or nil if the invalidation was explicit.
244 open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
245 sessionDidBecomeInvalidWithError?(session, error)
248 /// Requests credentials from the delegate in response to a session-level authentication request from the
251 /// - parameter session: The session containing the task that requested authentication.
252 /// - parameter challenge: An object that contains the request for authentication.
253 /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
255 open func urlSession(
256 _ session: URLSession,
257 didReceive challenge: URLAuthenticationChallenge,
258 completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
260 guard sessionDidReceiveChallengeWithCompletion == nil else {
261 sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
265 var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
266 var credential: URLCredential?
268 if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
269 (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
270 } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
271 let host = challenge.protectionSpace.host
274 let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
275 let serverTrust = challenge.protectionSpace.serverTrust
277 if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
278 disposition = .useCredential
279 credential = URLCredential(trust: serverTrust)
281 disposition = .cancelAuthenticationChallenge
286 completionHandler(disposition, credential)
291 /// Tells the delegate that all messages enqueued for a session have been delivered.
293 /// - parameter session: The session that no longer has any outstanding requests.
294 open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
295 sessionDidFinishEventsForBackgroundURLSession?(session)
301 // MARK: - URLSessionTaskDelegate
303 extension SessionDelegate: URLSessionTaskDelegate {
304 /// Tells the delegate that the remote server requested an HTTP redirect.
306 /// - parameter session: The session containing the task whose request resulted in a redirect.
307 /// - parameter task: The task whose request resulted in a redirect.
308 /// - parameter response: An object containing the server’s response to the original request.
309 /// - parameter request: A URL request object filled out with the new location.
310 /// - parameter completionHandler: A closure that your handler should call with either the value of the request
311 /// parameter, a modified URL request object, or NULL to refuse the redirect and
312 /// return the body of the redirect response.
313 open func urlSession(
314 _ session: URLSession,
315 task: URLSessionTask,
316 willPerformHTTPRedirection response: HTTPURLResponse,
317 newRequest request: URLRequest,
318 completionHandler: @escaping (URLRequest?) -> Void)
320 guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
321 taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
325 var redirectRequest: URLRequest? = request
327 if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
328 redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
331 completionHandler(redirectRequest)
334 /// Requests credentials from the delegate in response to an authentication request from the remote server.
336 /// - parameter session: The session containing the task whose request requires authentication.
337 /// - parameter task: The task whose request requires authentication.
338 /// - parameter challenge: An object that contains the request for authentication.
339 /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
341 open func urlSession(
342 _ session: URLSession,
343 task: URLSessionTask,
344 didReceive challenge: URLAuthenticationChallenge,
345 completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
347 guard taskDidReceiveChallengeWithCompletion == nil else {
348 taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
352 if let taskDidReceiveChallenge = taskDidReceiveChallenge {
353 let result = taskDidReceiveChallenge(session, task, challenge)
354 completionHandler(result.0, result.1)
355 } else if let delegate = self[task]?.delegate {
359 didReceive: challenge,
360 completionHandler: completionHandler
363 urlSession(session, didReceive: challenge, completionHandler: completionHandler)
367 /// Tells the delegate when a task requires a new request body stream to send to the remote server.
369 /// - parameter session: The session containing the task that needs a new body stream.
370 /// - parameter task: The task that needs a new body stream.
371 /// - parameter completionHandler: A completion handler that your delegate method should call with the new body stream.
372 open func urlSession(
373 _ session: URLSession,
374 task: URLSessionTask,
375 needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
377 guard taskNeedNewBodyStreamWithCompletion == nil else {
378 taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
382 if let taskNeedNewBodyStream = taskNeedNewBodyStream {
383 completionHandler(taskNeedNewBodyStream(session, task))
384 } else if let delegate = self[task]?.delegate {
385 delegate.urlSession(session, task: task, needNewBodyStream: completionHandler)
389 /// Periodically informs the delegate of the progress of sending body content to the server.
391 /// - parameter session: The session containing the data task.
392 /// - parameter task: The data task.
393 /// - parameter bytesSent: The number of bytes sent since the last time this delegate method was called.
394 /// - parameter totalBytesSent: The total number of bytes sent so far.
395 /// - parameter totalBytesExpectedToSend: The expected length of the body data.
396 open func urlSession(
397 _ session: URLSession,
398 task: URLSessionTask,
399 didSendBodyData bytesSent: Int64,
400 totalBytesSent: Int64,
401 totalBytesExpectedToSend: Int64)
403 if let taskDidSendBodyData = taskDidSendBodyData {
404 taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
405 } else if let delegate = self[task]?.delegate as? UploadTaskDelegate {
409 didSendBodyData: bytesSent,
410 totalBytesSent: totalBytesSent,
411 totalBytesExpectedToSend: totalBytesExpectedToSend
418 /// Tells the delegate that the session finished collecting metrics for the task.
420 /// - parameter session: The session collecting the metrics.
421 /// - parameter task: The task whose metrics have been collected.
422 /// - parameter metrics: The collected metrics.
423 @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
424 @objc(URLSession:task:didFinishCollectingMetrics:)
425 open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
426 self[task]?.delegate.metrics = metrics
431 /// Tells the delegate that the task finished transferring data.
433 /// - parameter session: The session containing the task whose request finished transferring data.
434 /// - parameter task: The task whose request finished transferring data.
435 /// - parameter error: If an error occurred, an error object indicating how the transfer failed, otherwise nil.
436 open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
437 /// Executed after it is determined that the request is not going to be retried
438 let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
439 guard let strongSelf = self else { return }
441 strongSelf.taskDidComplete?(session, task, error)
443 strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
445 NotificationCenter.default.post(
446 name: Notification.Name.Task.DidComplete,
448 userInfo: [Notification.Key.Task: task]
451 strongSelf[task] = nil
454 guard let request = self[task], let sessionManager = sessionManager else {
455 completeTask(session, task, error)
459 // Run all validations on the request before checking if an error occurred
460 request.validations.forEach { $0() }
462 // Determine whether an error has occurred
463 var error: Error? = error
465 if request.delegate.error != nil {
466 error = request.delegate.error
469 /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
470 /// should be retried. Otherwise, complete the task by notifying the task delegate.
471 if let retrier = retrier, let error = error {
472 retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
473 guard shouldRetry else { completeTask(session, task, error) ; return }
475 DispatchQueue.utility.after(timeDelay) { [weak self] in
476 guard let strongSelf = self else { return }
478 let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false
480 if retrySucceeded, let task = request.task {
481 strongSelf[task] = request
484 completeTask(session, task, error)
489 completeTask(session, task, error)
494 // MARK: - URLSessionDataDelegate
496 extension SessionDelegate: URLSessionDataDelegate {
497 /// Tells the delegate that the data task received the initial reply (headers) from the server.
499 /// - parameter session: The session containing the data task that received an initial reply.
500 /// - parameter dataTask: The data task that received an initial reply.
501 /// - parameter response: A URL response object populated with headers.
502 /// - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a
503 /// constant to indicate whether the transfer should continue as a data task or
504 /// should become a download task.
505 open func urlSession(
506 _ session: URLSession,
507 dataTask: URLSessionDataTask,
508 didReceive response: URLResponse,
509 completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
511 guard dataTaskDidReceiveResponseWithCompletion == nil else {
512 dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
516 var disposition: URLSession.ResponseDisposition = .allow
518 if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
519 disposition = dataTaskDidReceiveResponse(session, dataTask, response)
522 completionHandler(disposition)
525 /// Tells the delegate that the data task was changed to a download task.
527 /// - parameter session: The session containing the task that was replaced by a download task.
528 /// - parameter dataTask: The data task that was replaced by a download task.
529 /// - parameter downloadTask: The new download task that replaced the data task.
530 open func urlSession(
531 _ session: URLSession,
532 dataTask: URLSessionDataTask,
533 didBecome downloadTask: URLSessionDownloadTask)
535 if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
536 dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
538 self[downloadTask]?.delegate = DownloadTaskDelegate(task: downloadTask)
542 /// Tells the delegate that the data task has received some of the expected data.
544 /// - parameter session: The session containing the data task that provided data.
545 /// - parameter dataTask: The data task that provided data.
546 /// - parameter data: A data object containing the transferred data.
547 open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
548 if let dataTaskDidReceiveData = dataTaskDidReceiveData {
549 dataTaskDidReceiveData(session, dataTask, data)
550 } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
551 delegate.urlSession(session, dataTask: dataTask, didReceive: data)
555 /// Asks the delegate whether the data (or upload) task should store the response in the cache.
557 /// - parameter session: The session containing the data (or upload) task.
558 /// - parameter dataTask: The data (or upload) task.
559 /// - parameter proposedResponse: The default caching behavior. This behavior is determined based on the current
560 /// caching policy and the values of certain received headers, such as the Pragma
561 /// and Cache-Control headers.
562 /// - parameter completionHandler: A block that your handler must call, providing either the original proposed
563 /// response, a modified version of that response, or NULL to prevent caching the
564 /// response. If your delegate implements this method, it must call this completion
565 /// handler; otherwise, your app leaks memory.
566 open func urlSession(
567 _ session: URLSession,
568 dataTask: URLSessionDataTask,
569 willCacheResponse proposedResponse: CachedURLResponse,
570 completionHandler: @escaping (CachedURLResponse?) -> Void)
572 guard dataTaskWillCacheResponseWithCompletion == nil else {
573 dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
577 if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
578 completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
579 } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
583 willCacheResponse: proposedResponse,
584 completionHandler: completionHandler
587 completionHandler(proposedResponse)
592 // MARK: - URLSessionDownloadDelegate
594 extension SessionDelegate: URLSessionDownloadDelegate {
595 /// Tells the delegate that a download task has finished downloading.
597 /// - parameter session: The session containing the download task that finished.
598 /// - parameter downloadTask: The download task that finished.
599 /// - parameter location: A file URL for the temporary file. Because the file is temporary, you must either
600 /// open the file for reading or move it to a permanent location in your app’s sandbox
601 /// container directory before returning from this delegate method.
602 open func urlSession(
603 _ session: URLSession,
604 downloadTask: URLSessionDownloadTask,
605 didFinishDownloadingTo location: URL)
607 if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
608 downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
609 } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
610 delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
614 /// Periodically informs the delegate about the download’s progress.
616 /// - parameter session: The session containing the download task.
617 /// - parameter downloadTask: The download task.
618 /// - parameter bytesWritten: The number of bytes transferred since the last time this delegate
619 /// method was called.
620 /// - parameter totalBytesWritten: The total number of bytes transferred so far.
621 /// - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length
622 /// header. If this header was not provided, the value is
623 /// `NSURLSessionTransferSizeUnknown`.
624 open func urlSession(
625 _ session: URLSession,
626 downloadTask: URLSessionDownloadTask,
627 didWriteData bytesWritten: Int64,
628 totalBytesWritten: Int64,
629 totalBytesExpectedToWrite: Int64)
631 if let downloadTaskDidWriteData = downloadTaskDidWriteData {
632 downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
633 } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
636 downloadTask: downloadTask,
637 didWriteData: bytesWritten,
638 totalBytesWritten: totalBytesWritten,
639 totalBytesExpectedToWrite: totalBytesExpectedToWrite
644 /// Tells the delegate that the download task has resumed downloading.
646 /// - parameter session: The session containing the download task that finished.
647 /// - parameter downloadTask: The download task that resumed. See explanation in the discussion.
648 /// - parameter fileOffset: If the file's cache policy or last modified date prevents reuse of the
649 /// existing content, then this value is zero. Otherwise, this value is an
650 /// integer representing the number of bytes on disk that do not need to be
652 /// - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header.
653 /// If this header was not provided, the value is NSURLSessionTransferSizeUnknown.
654 open func urlSession(
655 _ session: URLSession,
656 downloadTask: URLSessionDownloadTask,
657 didResumeAtOffset fileOffset: Int64,
658 expectedTotalBytes: Int64)
660 if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
661 downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
662 } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
665 downloadTask: downloadTask,
666 didResumeAtOffset: fileOffset,
667 expectedTotalBytes: expectedTotalBytes
673 // MARK: - URLSessionStreamDelegate
677 @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
678 extension SessionDelegate: URLSessionStreamDelegate {
679 /// Tells the delegate that the read side of the connection has been closed.
681 /// - parameter session: The session.
682 /// - parameter streamTask: The stream task.
683 open func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) {
684 streamTaskReadClosed?(session, streamTask)
687 /// Tells the delegate that the write side of the connection has been closed.
689 /// - parameter session: The session.
690 /// - parameter streamTask: The stream task.
691 open func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) {
692 streamTaskWriteClosed?(session, streamTask)
695 /// Tells the delegate that the system has determined that a better route to the host is available.
697 /// - parameter session: The session.
698 /// - parameter streamTask: The stream task.
699 open func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) {
700 streamTaskBetterRouteDiscovered?(session, streamTask)
703 /// Tells the delegate that the stream task has been completed and provides the unopened stream objects.
705 /// - parameter session: The session.
706 /// - parameter streamTask: The stream task.
707 /// - parameter inputStream: The new input stream.
708 /// - parameter outputStream: The new output stream.
709 open func urlSession(
710 _ session: URLSession,
711 streamTask: URLSessionStreamTask,
712 didBecome inputStream: InputStream,
713 outputStream: OutputStream)
715 streamTaskDidBecomeInputAndOutputStreams?(session, streamTask, inputStream, outputStream)