SessionDelegate.swift 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. //
  2. // SessionDelegate.swift
  3. //
  4. // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
  5. //
  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:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  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
  22. // THE SOFTWARE.
  23. //
  24. import Foundation
  25. /// Responsible for handling all delegate callbacks for the underlying session.
  26. open class SessionDelegate: NSObject {
  27. // MARK: URLSessionDelegate Overrides
  28. /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
  29. open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
  30. /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
  31. open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
  32. /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
  33. open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
  34. /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`.
  35. open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?
  36. // MARK: URLSessionTaskDelegate Overrides
  37. /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`.
  38. open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
  39. /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and
  40. /// requires the caller to call the `completionHandler`.
  41. open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)?
  42. /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`.
  43. open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
  44. /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and
  45. /// requires the caller to call the `completionHandler`.
  46. open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
  47. /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`.
  48. open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
  49. /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and
  50. /// requires the caller to call the `completionHandler`.
  51. open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)?
  52. /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`.
  53. open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
  54. /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`.
  55. open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
  56. // MARK: URLSessionDataDelegate Overrides
  57. /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`.
  58. open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
  59. /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and
  60. /// requires caller to call the `completionHandler`.
  61. open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)?
  62. /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`.
  63. open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
  64. /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`.
  65. open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
  66. /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`.
  67. open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
  68. /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and
  69. /// requires caller to call the `completionHandler`.
  70. open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)?
  71. // MARK: URLSessionDownloadDelegate Overrides
  72. /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`.
  73. open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?
  74. /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`.
  75. open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
  76. /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`.
  77. open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
  78. // MARK: URLSessionStreamDelegate Overrides
  79. #if !os(watchOS)
  80. /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`.
  81. open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)?
  82. /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`.
  83. open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)?
  84. /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`.
  85. open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)?
  86. /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`.
  87. open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)?
  88. #endif
  89. // MARK: Properties
  90. var retrier: RequestRetrier?
  91. weak var sessionManager: SessionManager?
  92. private var protectedRequests = Protector<[Int: Request]>([:])
  93. /// Access the task delegate for the specified task in a thread-safe manner.
  94. open subscript(task: URLSessionTask) -> Request? {
  95. get { return protectedRequests.read { $0[task.taskIdentifier] } }
  96. set { protectedRequests.write { $0[task.taskIdentifier] = newValue } }
  97. }
  98. // MARK: Lifecycle
  99. /// Initializes the `SessionDelegate` instance.
  100. ///
  101. /// - returns: The new `SessionDelegate` instance.
  102. public override init() {
  103. super.init()
  104. }
  105. // MARK: NSObject Overrides
  106. /// Returns a `Bool` indicating whether the `SessionDelegate` implements or inherits a method that can respond
  107. /// to a specified message.
  108. ///
  109. /// - parameter selector: A selector that identifies a message.
  110. ///
  111. /// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`.
  112. open override func responds(to selector: Selector) -> Bool {
  113. #if !os(macOS)
  114. if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
  115. return sessionDidFinishEventsForBackgroundURLSession != nil
  116. }
  117. #endif
  118. #if !os(watchOS)
  119. switch selector {
  120. case #selector(URLSessionStreamDelegate.urlSession(_:readClosedFor:)):
  121. return streamTaskReadClosed != nil
  122. case #selector(URLSessionStreamDelegate.urlSession(_:writeClosedFor:)):
  123. return streamTaskWriteClosed != nil
  124. case #selector(URLSessionStreamDelegate.urlSession(_:betterRouteDiscoveredFor:)):
  125. return streamTaskBetterRouteDiscovered != nil
  126. case #selector(URLSessionStreamDelegate.urlSession(_:streamTask:didBecome:outputStream:)):
  127. return streamTaskDidBecomeInputAndOutputStreams != nil
  128. default:
  129. break
  130. }
  131. #endif
  132. switch selector {
  133. case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)):
  134. return sessionDidBecomeInvalidWithError != nil
  135. case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)):
  136. return (sessionDidReceiveChallenge != nil || sessionDidReceiveChallengeWithCompletion != nil)
  137. case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
  138. return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
  139. case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)):
  140. return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
  141. default:
  142. return type(of: self).instancesRespond(to: selector)
  143. }
  144. }
  145. }
  146. // MARK: - URLSessionDelegate
  147. extension SessionDelegate: URLSessionDelegate {
  148. /// Tells the delegate that the session has been invalidated.
  149. ///
  150. /// - parameter session: The session object that was invalidated.
  151. /// - parameter error: The error that caused invalidation, or nil if the invalidation was explicit.
  152. open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
  153. sessionDidBecomeInvalidWithError?(session, error)
  154. }
  155. /// Requests credentials from the delegate in response to a session-level authentication request from the
  156. /// remote server.
  157. ///
  158. /// - parameter session: The session containing the task that requested authentication.
  159. /// - parameter challenge: An object that contains the request for authentication.
  160. /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
  161. /// and credential.
  162. open func urlSession(
  163. _ session: URLSession,
  164. didReceive challenge: URLAuthenticationChallenge,
  165. completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
  166. {
  167. guard sessionDidReceiveChallengeWithCompletion == nil else {
  168. sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
  169. return
  170. }
  171. var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
  172. var credential: URLCredential?
  173. if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
  174. (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
  175. } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
  176. let host = challenge.protectionSpace.host
  177. if
  178. let serverTrustEvaluators = session.serverTrustManager?.serverTrustEvaluators(forHost: host),
  179. let serverTrust = challenge.protectionSpace.serverTrust
  180. {
  181. if serverTrustEvaluators.evaluate(serverTrust, forHost: host) {
  182. disposition = .useCredential
  183. credential = URLCredential(trust: serverTrust)
  184. } else {
  185. disposition = .cancelAuthenticationChallenge
  186. }
  187. }
  188. }
  189. completionHandler(disposition, credential)
  190. }
  191. #if !os(macOS)
  192. /// Tells the delegate that all messages enqueued for a session have been delivered.
  193. ///
  194. /// - parameter session: The session that no longer has any outstanding requests.
  195. open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
  196. sessionDidFinishEventsForBackgroundURLSession?(session)
  197. }
  198. #endif
  199. }
  200. // MARK: - URLSessionTaskDelegate
  201. extension SessionDelegate: URLSessionTaskDelegate {
  202. /// Tells the delegate that the remote server requested an HTTP redirect.
  203. ///
  204. /// - parameter session: The session containing the task whose request resulted in a redirect.
  205. /// - parameter task: The task whose request resulted in a redirect.
  206. /// - parameter response: An object containing the server’s response to the original request.
  207. /// - parameter request: A URL request object filled out with the new location.
  208. /// - parameter completionHandler: A closure that your handler should call with either the value of the request
  209. /// parameter, a modified URL request object, or NULL to refuse the redirect and
  210. /// return the body of the redirect response.
  211. open func urlSession(
  212. _ session: URLSession,
  213. task: URLSessionTask,
  214. willPerformHTTPRedirection response: HTTPURLResponse,
  215. newRequest request: URLRequest,
  216. completionHandler: @escaping (URLRequest?) -> Void)
  217. {
  218. guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
  219. taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
  220. return
  221. }
  222. var redirectRequest: URLRequest? = request
  223. if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
  224. redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
  225. }
  226. completionHandler(redirectRequest)
  227. }
  228. /// Requests credentials from the delegate in response to an authentication request from the remote server.
  229. ///
  230. /// - parameter session: The session containing the task whose request requires authentication.
  231. /// - parameter task: The task whose request requires authentication.
  232. /// - parameter challenge: An object that contains the request for authentication.
  233. /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
  234. /// and credential.
  235. open func urlSession(
  236. _ session: URLSession,
  237. task: URLSessionTask,
  238. didReceive challenge: URLAuthenticationChallenge,
  239. completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
  240. {
  241. guard taskDidReceiveChallengeWithCompletion == nil else {
  242. taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
  243. return
  244. }
  245. if let taskDidReceiveChallenge = taskDidReceiveChallenge {
  246. let result = taskDidReceiveChallenge(session, task, challenge)
  247. completionHandler(result.0, result.1)
  248. } else if let delegate = self[task]?.delegate {
  249. delegate.urlSession(
  250. session,
  251. task: task,
  252. didReceive: challenge,
  253. completionHandler: completionHandler
  254. )
  255. } else {
  256. urlSession(session, didReceive: challenge, completionHandler: completionHandler)
  257. }
  258. }
  259. /// Tells the delegate when a task requires a new request body stream to send to the remote server.
  260. ///
  261. /// - parameter session: The session containing the task that needs a new body stream.
  262. /// - parameter task: The task that needs a new body stream.
  263. /// - parameter completionHandler: A completion handler that your delegate method should call with the new body stream.
  264. open func urlSession(
  265. _ session: URLSession,
  266. task: URLSessionTask,
  267. needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
  268. {
  269. guard taskNeedNewBodyStreamWithCompletion == nil else {
  270. taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
  271. return
  272. }
  273. if let taskNeedNewBodyStream = taskNeedNewBodyStream {
  274. completionHandler(taskNeedNewBodyStream(session, task))
  275. } else if let delegate = self[task]?.delegate {
  276. delegate.urlSession(session, task: task, needNewBodyStream: completionHandler)
  277. }
  278. }
  279. /// Periodically informs the delegate of the progress of sending body content to the server.
  280. ///
  281. /// - parameter session: The session containing the data task.
  282. /// - parameter task: The data task.
  283. /// - parameter bytesSent: The number of bytes sent since the last time this delegate method was called.
  284. /// - parameter totalBytesSent: The total number of bytes sent so far.
  285. /// - parameter totalBytesExpectedToSend: The expected length of the body data.
  286. open func urlSession(
  287. _ session: URLSession,
  288. task: URLSessionTask,
  289. didSendBodyData bytesSent: Int64,
  290. totalBytesSent: Int64,
  291. totalBytesExpectedToSend: Int64)
  292. {
  293. if let taskDidSendBodyData = taskDidSendBodyData {
  294. taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
  295. } else if let delegate = self[task]?.delegate as? UploadTaskDelegate {
  296. delegate.URLSession(
  297. session,
  298. task: task,
  299. didSendBodyData: bytesSent,
  300. totalBytesSent: totalBytesSent,
  301. totalBytesExpectedToSend: totalBytesExpectedToSend
  302. )
  303. }
  304. }
  305. #if !os(watchOS)
  306. /// Tells the delegate that the session finished collecting metrics for the task.
  307. ///
  308. /// - parameter session: The session collecting the metrics.
  309. /// - parameter task: The task whose metrics have been collected.
  310. /// - parameter metrics: The collected metrics.
  311. @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
  312. @objc(URLSession:task:didFinishCollectingMetrics:)
  313. open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
  314. self[task]?.delegate.metrics = metrics
  315. }
  316. #endif
  317. /// Tells the delegate that the task finished transferring data.
  318. ///
  319. /// - parameter session: The session containing the task whose request finished transferring data.
  320. /// - parameter task: The task whose request finished transferring data.
  321. /// - parameter error: If an error occurred, an error object indicating how the transfer failed, otherwise nil.
  322. open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
  323. /// Executed after it is determined that the request is not going to be retried
  324. let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
  325. guard let strongSelf = self else { return }
  326. strongSelf.taskDidComplete?(session, task, error)
  327. strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
  328. var userInfo: [String: Any] = [Notification.Key.Task: task]
  329. if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data {
  330. userInfo[Notification.Key.ResponseData] = data
  331. }
  332. NotificationCenter.default.post(
  333. name: Notification.Name.Task.DidComplete,
  334. object: strongSelf,
  335. userInfo: userInfo
  336. )
  337. strongSelf[task] = nil
  338. }
  339. guard let request = self[task], let sessionManager = sessionManager else {
  340. completeTask(session, task, error)
  341. return
  342. }
  343. // Run all validations on the request before checking if an error occurred
  344. request.validations.forEach { $0() }
  345. // Determine whether an error has occurred
  346. var error: Error? = error
  347. if request.delegate.error != nil {
  348. error = request.delegate.error
  349. }
  350. /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
  351. /// should be retried. Otherwise, complete the task by notifying the task delegate.
  352. if let retrier = retrier, let error = error {
  353. retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
  354. guard shouldRetry else { completeTask(session, task, error) ; return }
  355. DispatchQueue.utility.after(timeDelay) { [weak self] in
  356. guard let strongSelf = self else { return }
  357. let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false
  358. if retrySucceeded, let task = request.task {
  359. strongSelf[task] = request
  360. return
  361. } else {
  362. completeTask(session, task, error)
  363. }
  364. }
  365. }
  366. } else {
  367. completeTask(session, task, error)
  368. }
  369. }
  370. }
  371. // MARK: - URLSessionDataDelegate
  372. extension SessionDelegate: URLSessionDataDelegate {
  373. /// Tells the delegate that the data task received the initial reply (headers) from the server.
  374. ///
  375. /// - parameter session: The session containing the data task that received an initial reply.
  376. /// - parameter dataTask: The data task that received an initial reply.
  377. /// - parameter response: A URL response object populated with headers.
  378. /// - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a
  379. /// constant to indicate whether the transfer should continue as a data task or
  380. /// should become a download task.
  381. open func urlSession(
  382. _ session: URLSession,
  383. dataTask: URLSessionDataTask,
  384. didReceive response: URLResponse,
  385. completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
  386. {
  387. guard dataTaskDidReceiveResponseWithCompletion == nil else {
  388. dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
  389. return
  390. }
  391. var disposition: URLSession.ResponseDisposition = .allow
  392. if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
  393. disposition = dataTaskDidReceiveResponse(session, dataTask, response)
  394. }
  395. completionHandler(disposition)
  396. }
  397. /// Tells the delegate that the data task was changed to a download task.
  398. ///
  399. /// - parameter session: The session containing the task that was replaced by a download task.
  400. /// - parameter dataTask: The data task that was replaced by a download task.
  401. /// - parameter downloadTask: The new download task that replaced the data task.
  402. open func urlSession(
  403. _ session: URLSession,
  404. dataTask: URLSessionDataTask,
  405. didBecome downloadTask: URLSessionDownloadTask)
  406. {
  407. if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
  408. dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
  409. } else {
  410. self[downloadTask]?.delegate = DownloadTaskDelegate(task: downloadTask)
  411. }
  412. }
  413. /// Tells the delegate that the data task has received some of the expected data.
  414. ///
  415. /// - parameter session: The session containing the data task that provided data.
  416. /// - parameter dataTask: The data task that provided data.
  417. /// - parameter data: A data object containing the transferred data.
  418. open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
  419. if let dataTaskDidReceiveData = dataTaskDidReceiveData {
  420. dataTaskDidReceiveData(session, dataTask, data)
  421. } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
  422. delegate.urlSession(session, dataTask: dataTask, didReceive: data)
  423. }
  424. }
  425. /// Asks the delegate whether the data (or upload) task should store the response in the cache.
  426. ///
  427. /// - parameter session: The session containing the data (or upload) task.
  428. /// - parameter dataTask: The data (or upload) task.
  429. /// - parameter proposedResponse: The default caching behavior. This behavior is determined based on the current
  430. /// caching policy and the values of certain received headers, such as the Pragma
  431. /// and Cache-Control headers.
  432. /// - parameter completionHandler: A block that your handler must call, providing either the original proposed
  433. /// response, a modified version of that response, or NULL to prevent caching the
  434. /// response. If your delegate implements this method, it must call this completion
  435. /// handler; otherwise, your app leaks memory.
  436. open func urlSession(
  437. _ session: URLSession,
  438. dataTask: URLSessionDataTask,
  439. willCacheResponse proposedResponse: CachedURLResponse,
  440. completionHandler: @escaping (CachedURLResponse?) -> Void)
  441. {
  442. guard dataTaskWillCacheResponseWithCompletion == nil else {
  443. dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
  444. return
  445. }
  446. if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
  447. completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
  448. } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
  449. delegate.urlSession(
  450. session,
  451. dataTask: dataTask,
  452. willCacheResponse: proposedResponse,
  453. completionHandler: completionHandler
  454. )
  455. } else {
  456. completionHandler(proposedResponse)
  457. }
  458. }
  459. }
  460. // MARK: - URLSessionDownloadDelegate
  461. extension SessionDelegate: URLSessionDownloadDelegate {
  462. /// Tells the delegate that a download task has finished downloading.
  463. ///
  464. /// - parameter session: The session containing the download task that finished.
  465. /// - parameter downloadTask: The download task that finished.
  466. /// - parameter location: A file URL for the temporary file. Because the file is temporary, you must either
  467. /// open the file for reading or move it to a permanent location in your app’s sandbox
  468. /// container directory before returning from this delegate method.
  469. open func urlSession(
  470. _ session: URLSession,
  471. downloadTask: URLSessionDownloadTask,
  472. didFinishDownloadingTo location: URL)
  473. {
  474. if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
  475. downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
  476. } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
  477. delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
  478. }
  479. }
  480. /// Periodically informs the delegate about the download’s progress.
  481. ///
  482. /// - parameter session: The session containing the download task.
  483. /// - parameter downloadTask: The download task.
  484. /// - parameter bytesWritten: The number of bytes transferred since the last time this delegate
  485. /// method was called.
  486. /// - parameter totalBytesWritten: The total number of bytes transferred so far.
  487. /// - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length
  488. /// header. If this header was not provided, the value is
  489. /// `NSURLSessionTransferSizeUnknown`.
  490. open func urlSession(
  491. _ session: URLSession,
  492. downloadTask: URLSessionDownloadTask,
  493. didWriteData bytesWritten: Int64,
  494. totalBytesWritten: Int64,
  495. totalBytesExpectedToWrite: Int64)
  496. {
  497. if let downloadTaskDidWriteData = downloadTaskDidWriteData {
  498. downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
  499. } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
  500. delegate.urlSession(
  501. session,
  502. downloadTask: downloadTask,
  503. didWriteData: bytesWritten,
  504. totalBytesWritten: totalBytesWritten,
  505. totalBytesExpectedToWrite: totalBytesExpectedToWrite
  506. )
  507. }
  508. }
  509. /// Tells the delegate that the download task has resumed downloading.
  510. ///
  511. /// - parameter session: The session containing the download task that finished.
  512. /// - parameter downloadTask: The download task that resumed. See explanation in the discussion.
  513. /// - parameter fileOffset: If the file's cache policy or last modified date prevents reuse of the
  514. /// existing content, then this value is zero. Otherwise, this value is an
  515. /// integer representing the number of bytes on disk that do not need to be
  516. /// retrieved again.
  517. /// - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header.
  518. /// If this header was not provided, the value is NSURLSessionTransferSizeUnknown.
  519. open func urlSession(
  520. _ session: URLSession,
  521. downloadTask: URLSessionDownloadTask,
  522. didResumeAtOffset fileOffset: Int64,
  523. expectedTotalBytes: Int64)
  524. {
  525. if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
  526. downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
  527. } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
  528. delegate.urlSession(
  529. session,
  530. downloadTask: downloadTask,
  531. didResumeAtOffset: fileOffset,
  532. expectedTotalBytes: expectedTotalBytes
  533. )
  534. }
  535. }
  536. }
  537. // MARK: - URLSessionStreamDelegate
  538. #if !os(watchOS)
  539. extension SessionDelegate: URLSessionStreamDelegate {
  540. /// Tells the delegate that the read side of the connection has been closed.
  541. ///
  542. /// - parameter session: The session.
  543. /// - parameter streamTask: The stream task.
  544. open func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) {
  545. streamTaskReadClosed?(session, streamTask)
  546. }
  547. /// Tells the delegate that the write side of the connection has been closed.
  548. ///
  549. /// - parameter session: The session.
  550. /// - parameter streamTask: The stream task.
  551. open func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) {
  552. streamTaskWriteClosed?(session, streamTask)
  553. }
  554. /// Tells the delegate that the system has determined that a better route to the host is available.
  555. ///
  556. /// - parameter session: The session.
  557. /// - parameter streamTask: The stream task.
  558. open func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) {
  559. streamTaskBetterRouteDiscovered?(session, streamTask)
  560. }
  561. /// Tells the delegate that the stream task has been completed and provides the unopened stream objects.
  562. ///
  563. /// - parameter session: The session.
  564. /// - parameter streamTask: The stream task.
  565. /// - parameter inputStream: The new input stream.
  566. /// - parameter outputStream: The new output stream.
  567. open func urlSession(
  568. _ session: URLSession,
  569. streamTask: URLSessionStreamTask,
  570. didBecome inputStream: InputStream,
  571. outputStream: OutputStream)
  572. {
  573. streamTaskDidBecomeInputAndOutputStreams?(session, streamTask, inputStream, outputStream)
  574. }
  575. }
  576. #endif