You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

725 lines
35 KiB

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