SessionDelegate.swift 30 KB

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