123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725 |
- //
- // SessionDelegate.swift
- //
- // Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- import Foundation
- /// Responsible for handling all delegate callbacks for the underlying session.
- open class SessionDelegate: NSObject {
- // MARK: URLSessionDelegate Overrides
- /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
- open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
- /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
- open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
- /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
- open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
- /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`.
- open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?
- // MARK: URLSessionTaskDelegate Overrides
- /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`.
- open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
- /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and
- /// requires the caller to call the `completionHandler`.
- open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)?
- /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`.
- open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
- /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and
- /// requires the caller to call the `completionHandler`.
- open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
- /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`.
- open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
- /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and
- /// requires the caller to call the `completionHandler`.
- open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)?
- /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`.
- open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
- /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`.
- open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
- // MARK: URLSessionDataDelegate Overrides
- /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`.
- open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
- /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and
- /// requires caller to call the `completionHandler`.
- open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)?
- /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`.
- open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
- /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`.
- open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
- /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`.
- open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
- /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and
- /// requires caller to call the `completionHandler`.
- open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)?
- // MARK: URLSessionDownloadDelegate Overrides
- /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`.
- open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?
- /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`.
- open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
- /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`.
- open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
- // MARK: URLSessionStreamDelegate Overrides
- #if !os(watchOS)
- /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`.
- @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
- open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
- get {
- return _streamTaskReadClosed as? (URLSession, URLSessionStreamTask) -> Void
- }
- set {
- _streamTaskReadClosed = newValue
- }
- }
- /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`.
- @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
- open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
- get {
- return _streamTaskWriteClosed as? (URLSession, URLSessionStreamTask) -> Void
- }
- set {
- _streamTaskWriteClosed = newValue
- }
- }
- /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`.
- @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
- open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)? {
- get {
- return _streamTaskBetterRouteDiscovered as? (URLSession, URLSessionStreamTask) -> Void
- }
- set {
- _streamTaskBetterRouteDiscovered = newValue
- }
- }
- /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`.
- @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
- open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)? {
- get {
- return _streamTaskDidBecomeInputStream as? (URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void
- }
- set {
- _streamTaskDidBecomeInputStream = newValue
- }
- }
- var _streamTaskReadClosed: Any?
- var _streamTaskWriteClosed: Any?
- var _streamTaskBetterRouteDiscovered: Any?
- var _streamTaskDidBecomeInputStream: Any?
- #endif
- // MARK: Properties
- var retrier: RequestRetrier?
- weak var sessionManager: SessionManager?
- var requests: [Int: Request] = [:]
- private let lock = NSLock()
- /// Access the task delegate for the specified task in a thread-safe manner.
- open subscript(task: URLSessionTask) -> Request? {
- get {
- lock.lock() ; defer { lock.unlock() }
- return requests[task.taskIdentifier]
- }
- set {
- lock.lock() ; defer { lock.unlock() }
- requests[task.taskIdentifier] = newValue
- }
- }
- // MARK: Lifecycle
- /// Initializes the `SessionDelegate` instance.
- ///
- /// - returns: The new `SessionDelegate` instance.
- public override init() {
- super.init()
- }
- // MARK: NSObject Overrides
- /// Returns a `Bool` indicating whether the `SessionDelegate` implements or inherits a method that can respond
- /// to a specified message.
- ///
- /// - parameter selector: A selector that identifies a message.
- ///
- /// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`.
- open override func responds(to selector: Selector) -> Bool {
- #if !os(macOS)
- if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
- return sessionDidFinishEventsForBackgroundURLSession != nil
- }
- #endif
- #if !os(watchOS)
- if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) {
- switch selector {
- case #selector(URLSessionStreamDelegate.urlSession(_:readClosedFor:)):
- return streamTaskReadClosed != nil
- case #selector(URLSessionStreamDelegate.urlSession(_:writeClosedFor:)):
- return streamTaskWriteClosed != nil
- case #selector(URLSessionStreamDelegate.urlSession(_:betterRouteDiscoveredFor:)):
- return streamTaskBetterRouteDiscovered != nil
- case #selector(URLSessionStreamDelegate.urlSession(_:streamTask:didBecome:outputStream:)):
- return streamTaskDidBecomeInputAndOutputStreams != nil
- default:
- break
- }
- }
- #endif
- switch selector {
- case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)):
- return sessionDidBecomeInvalidWithError != nil
- case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)):
- return (sessionDidReceiveChallenge != nil || sessionDidReceiveChallengeWithCompletion != nil)
- case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
- return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
- case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)):
- return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
- default:
- return type(of: self).instancesRespond(to: selector)
- }
- }
- }
- // MARK: - URLSessionDelegate
- extension SessionDelegate: URLSessionDelegate {
- /// Tells the delegate that the session has been invalidated.
- ///
- /// - parameter session: The session object that was invalidated.
- /// - parameter error: The error that caused invalidation, or nil if the invalidation was explicit.
- open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
- sessionDidBecomeInvalidWithError?(session, error)
- }
- /// Requests credentials from the delegate in response to a session-level authentication request from the
- /// remote server.
- ///
- /// - parameter session: The session containing the task that requested authentication.
- /// - parameter challenge: An object that contains the request for authentication.
- /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
- /// and credential.
- open func urlSession(
- _ session: URLSession,
- didReceive challenge: URLAuthenticationChallenge,
- completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
- {
- guard sessionDidReceiveChallengeWithCompletion == nil else {
- sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
- return
- }
- var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
- var credential: URLCredential?
- if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
- (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
- } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
- let host = challenge.protectionSpace.host
- if
- let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
- let serverTrust = challenge.protectionSpace.serverTrust
- {
- if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
- disposition = .useCredential
- credential = URLCredential(trust: serverTrust)
- } else {
- disposition = .cancelAuthenticationChallenge
- }
- }
- }
- completionHandler(disposition, credential)
- }
- #if !os(macOS)
- /// Tells the delegate that all messages enqueued for a session have been delivered.
- ///
- /// - parameter session: The session that no longer has any outstanding requests.
- open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
- sessionDidFinishEventsForBackgroundURLSession?(session)
- }
- #endif
- }
- // MARK: - URLSessionTaskDelegate
- extension SessionDelegate: URLSessionTaskDelegate {
- /// Tells the delegate that the remote server requested an HTTP redirect.
- ///
- /// - parameter session: The session containing the task whose request resulted in a redirect.
- /// - parameter task: The task whose request resulted in a redirect.
- /// - parameter response: An object containing the server’s response to the original request.
- /// - parameter request: A URL request object filled out with the new location.
- /// - parameter completionHandler: A closure that your handler should call with either the value of the request
- /// parameter, a modified URL request object, or NULL to refuse the redirect and
- /// return the body of the redirect response.
- open func urlSession(
- _ session: URLSession,
- task: URLSessionTask,
- willPerformHTTPRedirection response: HTTPURLResponse,
- newRequest request: URLRequest,
- completionHandler: @escaping (URLRequest?) -> Void)
- {
- guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
- taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
- return
- }
- var redirectRequest: URLRequest? = request
- if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
- redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
- }
- completionHandler(redirectRequest)
- }
- /// Requests credentials from the delegate in response to an authentication request from the remote server.
- ///
- /// - parameter session: The session containing the task whose request requires authentication.
- /// - parameter task: The task whose request requires authentication.
- /// - parameter challenge: An object that contains the request for authentication.
- /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
- /// and credential.
- open func urlSession(
- _ session: URLSession,
- task: URLSessionTask,
- didReceive challenge: URLAuthenticationChallenge,
- completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
- {
- guard taskDidReceiveChallengeWithCompletion == nil else {
- taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
- return
- }
- if let taskDidReceiveChallenge = taskDidReceiveChallenge {
- let result = taskDidReceiveChallenge(session, task, challenge)
- completionHandler(result.0, result.1)
- } else if let delegate = self[task]?.delegate {
- delegate.urlSession(
- session,
- task: task,
- didReceive: challenge,
- completionHandler: completionHandler
- )
- } else {
- urlSession(session, didReceive: challenge, completionHandler: completionHandler)
- }
- }
- /// Tells the delegate when a task requires a new request body stream to send to the remote server.
- ///
- /// - parameter session: The session containing the task that needs a new body stream.
- /// - parameter task: The task that needs a new body stream.
- /// - parameter completionHandler: A completion handler that your delegate method should call with the new body stream.
- open func urlSession(
- _ session: URLSession,
- task: URLSessionTask,
- needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
- {
- guard taskNeedNewBodyStreamWithCompletion == nil else {
- taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
- return
- }
- if let taskNeedNewBodyStream = taskNeedNewBodyStream {
- completionHandler(taskNeedNewBodyStream(session, task))
- } else if let delegate = self[task]?.delegate {
- delegate.urlSession(session, task: task, needNewBodyStream: completionHandler)
- }
- }
- /// Periodically informs the delegate of the progress of sending body content to the server.
- ///
- /// - parameter session: The session containing the data task.
- /// - parameter task: The data task.
- /// - parameter bytesSent: The number of bytes sent since the last time this delegate method was called.
- /// - parameter totalBytesSent: The total number of bytes sent so far.
- /// - parameter totalBytesExpectedToSend: The expected length of the body data.
- open func urlSession(
- _ session: URLSession,
- task: URLSessionTask,
- didSendBodyData bytesSent: Int64,
- totalBytesSent: Int64,
- totalBytesExpectedToSend: Int64)
- {
- if let taskDidSendBodyData = taskDidSendBodyData {
- taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
- } else if let delegate = self[task]?.delegate as? UploadTaskDelegate {
- delegate.URLSession(
- session,
- task: task,
- didSendBodyData: bytesSent,
- totalBytesSent: totalBytesSent,
- totalBytesExpectedToSend: totalBytesExpectedToSend
- )
- }
- }
- #if !os(watchOS)
- /// Tells the delegate that the session finished collecting metrics for the task.
- ///
- /// - parameter session: The session collecting the metrics.
- /// - parameter task: The task whose metrics have been collected.
- /// - parameter metrics: The collected metrics.
- @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
- @objc(URLSession:task:didFinishCollectingMetrics:)
- open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
- self[task]?.delegate.metrics = metrics
- }
- #endif
- /// Tells the delegate that the task finished transferring data.
- ///
- /// - parameter session: The session containing the task whose request finished transferring data.
- /// - parameter task: The task whose request finished transferring data.
- /// - parameter error: If an error occurred, an error object indicating how the transfer failed, otherwise nil.
- open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
- /// Executed after it is determined that the request is not going to be retried
- let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
- guard let strongSelf = self else { return }
- strongSelf.taskDidComplete?(session, task, error)
- strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
- var userInfo: [String: Any] = [Notification.Key.Task: task]
- if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data {
- userInfo[Notification.Key.ResponseData] = data
- }
- NotificationCenter.default.post(
- name: Notification.Name.Task.DidComplete,
- object: strongSelf,
- userInfo: userInfo
- )
- strongSelf[task] = nil
- }
- guard let request = self[task], let sessionManager = sessionManager else {
- completeTask(session, task, error)
- return
- }
- // Run all validations on the request before checking if an error occurred
- request.validations.forEach { $0() }
- // Determine whether an error has occurred
- var error: Error? = error
- if request.delegate.error != nil {
- error = request.delegate.error
- }
- /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
- /// should be retried. Otherwise, complete the task by notifying the task delegate.
- if let retrier = retrier, let error = error {
- retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
- guard shouldRetry else { completeTask(session, task, error) ; return }
- DispatchQueue.utility.after(timeDelay) { [weak self] in
- guard let strongSelf = self else { return }
- let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false
- if retrySucceeded, let task = request.task {
- strongSelf[task] = request
- return
- } else {
- completeTask(session, task, error)
- }
- }
- }
- } else {
- completeTask(session, task, error)
- }
- }
- }
- // MARK: - URLSessionDataDelegate
- extension SessionDelegate: URLSessionDataDelegate {
- /// Tells the delegate that the data task received the initial reply (headers) from the server.
- ///
- /// - parameter session: The session containing the data task that received an initial reply.
- /// - parameter dataTask: The data task that received an initial reply.
- /// - parameter response: A URL response object populated with headers.
- /// - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a
- /// constant to indicate whether the transfer should continue as a data task or
- /// should become a download task.
- open func urlSession(
- _ session: URLSession,
- dataTask: URLSessionDataTask,
- didReceive response: URLResponse,
- completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
- {
- guard dataTaskDidReceiveResponseWithCompletion == nil else {
- dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
- return
- }
- var disposition: URLSession.ResponseDisposition = .allow
- if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
- disposition = dataTaskDidReceiveResponse(session, dataTask, response)
- }
- completionHandler(disposition)
- }
- /// Tells the delegate that the data task was changed to a download task.
- ///
- /// - parameter session: The session containing the task that was replaced by a download task.
- /// - parameter dataTask: The data task that was replaced by a download task.
- /// - parameter downloadTask: The new download task that replaced the data task.
- open func urlSession(
- _ session: URLSession,
- dataTask: URLSessionDataTask,
- didBecome downloadTask: URLSessionDownloadTask)
- {
- if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
- dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
- } else {
- self[downloadTask]?.delegate = DownloadTaskDelegate(task: downloadTask)
- }
- }
- /// Tells the delegate that the data task has received some of the expected data.
- ///
- /// - parameter session: The session containing the data task that provided data.
- /// - parameter dataTask: The data task that provided data.
- /// - parameter data: A data object containing the transferred data.
- open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
- if let dataTaskDidReceiveData = dataTaskDidReceiveData {
- dataTaskDidReceiveData(session, dataTask, data)
- } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
- delegate.urlSession(session, dataTask: dataTask, didReceive: data)
- }
- }
- /// Asks the delegate whether the data (or upload) task should store the response in the cache.
- ///
- /// - parameter session: The session containing the data (or upload) task.
- /// - parameter dataTask: The data (or upload) task.
- /// - parameter proposedResponse: The default caching behavior. This behavior is determined based on the current
- /// caching policy and the values of certain received headers, such as the Pragma
- /// and Cache-Control headers.
- /// - parameter completionHandler: A block that your handler must call, providing either the original proposed
- /// response, a modified version of that response, or NULL to prevent caching the
- /// response. If your delegate implements this method, it must call this completion
- /// handler; otherwise, your app leaks memory.
- open func urlSession(
- _ session: URLSession,
- dataTask: URLSessionDataTask,
- willCacheResponse proposedResponse: CachedURLResponse,
- completionHandler: @escaping (CachedURLResponse?) -> Void)
- {
- guard dataTaskWillCacheResponseWithCompletion == nil else {
- dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
- return
- }
- if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
- completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
- } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
- delegate.urlSession(
- session,
- dataTask: dataTask,
- willCacheResponse: proposedResponse,
- completionHandler: completionHandler
- )
- } else {
- completionHandler(proposedResponse)
- }
- }
- }
- // MARK: - URLSessionDownloadDelegate
- extension SessionDelegate: URLSessionDownloadDelegate {
- /// Tells the delegate that a download task has finished downloading.
- ///
- /// - parameter session: The session containing the download task that finished.
- /// - parameter downloadTask: The download task that finished.
- /// - parameter location: A file URL for the temporary file. Because the file is temporary, you must either
- /// open the file for reading or move it to a permanent location in your app’s sandbox
- /// container directory before returning from this delegate method.
- open func urlSession(
- _ session: URLSession,
- downloadTask: URLSessionDownloadTask,
- didFinishDownloadingTo location: URL)
- {
- if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
- downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
- } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
- delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
- }
- }
- /// Periodically informs the delegate about the download’s progress.
- ///
- /// - parameter session: The session containing the download task.
- /// - parameter downloadTask: The download task.
- /// - parameter bytesWritten: The number of bytes transferred since the last time this delegate
- /// method was called.
- /// - parameter totalBytesWritten: The total number of bytes transferred so far.
- /// - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length
- /// header. If this header was not provided, the value is
- /// `NSURLSessionTransferSizeUnknown`.
- open func urlSession(
- _ session: URLSession,
- downloadTask: URLSessionDownloadTask,
- didWriteData bytesWritten: Int64,
- totalBytesWritten: Int64,
- totalBytesExpectedToWrite: Int64)
- {
- if let downloadTaskDidWriteData = downloadTaskDidWriteData {
- downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
- } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
- delegate.urlSession(
- session,
- downloadTask: downloadTask,
- didWriteData: bytesWritten,
- totalBytesWritten: totalBytesWritten,
- totalBytesExpectedToWrite: totalBytesExpectedToWrite
- )
- }
- }
- /// Tells the delegate that the download task has resumed downloading.
- ///
- /// - parameter session: The session containing the download task that finished.
- /// - parameter downloadTask: The download task that resumed. See explanation in the discussion.
- /// - parameter fileOffset: If the file's cache policy or last modified date prevents reuse of the
- /// existing content, then this value is zero. Otherwise, this value is an
- /// integer representing the number of bytes on disk that do not need to be
- /// retrieved again.
- /// - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header.
- /// If this header was not provided, the value is NSURLSessionTransferSizeUnknown.
- open func urlSession(
- _ session: URLSession,
- downloadTask: URLSessionDownloadTask,
- didResumeAtOffset fileOffset: Int64,
- expectedTotalBytes: Int64)
- {
- if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
- downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
- } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
- delegate.urlSession(
- session,
- downloadTask: downloadTask,
- didResumeAtOffset: fileOffset,
- expectedTotalBytes: expectedTotalBytes
- )
- }
- }
- }
- // MARK: - URLSessionStreamDelegate
- #if !os(watchOS)
- @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
- extension SessionDelegate: URLSessionStreamDelegate {
- /// Tells the delegate that the read side of the connection has been closed.
- ///
- /// - parameter session: The session.
- /// - parameter streamTask: The stream task.
- open func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) {
- streamTaskReadClosed?(session, streamTask)
- }
- /// Tells the delegate that the write side of the connection has been closed.
- ///
- /// - parameter session: The session.
- /// - parameter streamTask: The stream task.
- open func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) {
- streamTaskWriteClosed?(session, streamTask)
- }
- /// Tells the delegate that the system has determined that a better route to the host is available.
- ///
- /// - parameter session: The session.
- /// - parameter streamTask: The stream task.
- open func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) {
- streamTaskBetterRouteDiscovered?(session, streamTask)
- }
- /// Tells the delegate that the stream task has been completed and provides the unopened stream objects.
- ///
- /// - parameter session: The session.
- /// - parameter streamTask: The stream task.
- /// - parameter inputStream: The new input stream.
- /// - parameter outputStream: The new output stream.
- open func urlSession(
- _ session: URLSession,
- streamTask: URLSessionStreamTask,
- didBecome inputStream: InputStream,
- outputStream: OutputStream)
- {
- streamTaskDidBecomeInputAndOutputStreams?(session, streamTask, inputStream, outputStream)
- }
- }
- #endif
|