Manager.swift 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. // Manager.swift
  2. //
  3. // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. import Foundation
  23. /**
  24. Responsible for creating and managing `Request` objects, as well as their underlying `NSURLSession`.
  25. */
  26. public class Manager {
  27. // MARK: - Properties
  28. /**
  29. A shared instance of `Manager`, used by top-level Alamofire request methods, and suitable for use directly
  30. for any ad hoc requests.
  31. */
  32. public static let sharedInstance: Manager = {
  33. let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
  34. configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
  35. return Manager(configuration: configuration)
  36. }()
  37. /**
  38. Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers.
  39. */
  40. public static let defaultHTTPHeaders: [String: String] = {
  41. // Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3
  42. let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"
  43. // Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5
  44. let acceptLanguage = NSLocale.preferredLanguages().prefix(6).enumerate().map { index, languageCode in
  45. let quality = 1.0 - (Double(index) * 0.1)
  46. return "\(languageCode);q=\(quality)"
  47. }.joinWithSeparator(", ")
  48. // User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
  49. let userAgent: String = {
  50. if let info = NSBundle.mainBundle().infoDictionary {
  51. let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
  52. let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
  53. let version = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
  54. let os = NSProcessInfo.processInfo().operatingSystemVersionString
  55. var mutableUserAgent = NSMutableString(string: "\(executable)/\(bundle) (\(version); OS \(os))") as CFMutableString
  56. let transform = NSString(string: "Any-Latin; Latin-ASCII; [:^ASCII:] Remove") as CFString
  57. if CFStringTransform(mutableUserAgent, UnsafeMutablePointer<CFRange>(nil), transform, false) {
  58. return mutableUserAgent as String
  59. }
  60. }
  61. return "Alamofire"
  62. }()
  63. return [
  64. "Accept-Encoding": acceptEncoding,
  65. "Accept-Language": acceptLanguage,
  66. "User-Agent": userAgent
  67. ]
  68. }()
  69. let queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
  70. /// The underlying session.
  71. public let session: NSURLSession
  72. /// The session delegate handling all the task and session delegate callbacks.
  73. public let delegate: SessionDelegate
  74. /// Whether to start requests immediately after being constructed. `true` by default.
  75. public var startRequestsImmediately: Bool = true
  76. /**
  77. The background completion handler closure provided by the UIApplicationDelegate
  78. `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background
  79. completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation
  80. will automatically call the handler.
  81. If you need to handle your own events before the handler is called, then you need to override the
  82. SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished.
  83. `nil` by default.
  84. */
  85. public var backgroundCompletionHandler: (() -> Void)?
  86. // MARK: - Lifecycle
  87. /**
  88. Initializes the `Manager` instance with the specified configuration, delegate and server trust policy.
  89. - parameter configuration: The configuration used to construct the managed session.
  90. `NSURLSessionConfiguration.defaultSessionConfiguration()` by default.
  91. - parameter delegate: The delegate used when initializing the session. `SessionDelegate()` by
  92. default.
  93. - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
  94. challenges. `nil` by default.
  95. - returns: The new `Manager` instance.
  96. */
  97. public init(
  98. configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration(),
  99. delegate: SessionDelegate = SessionDelegate(),
  100. serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
  101. {
  102. self.delegate = delegate
  103. self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
  104. commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
  105. }
  106. /**
  107. Initializes the `Manager` instance with the specified session, delegate and server trust policy.
  108. - parameter session: The URL session.
  109. - parameter delegate: The delegate of the URL session. Must equal the URL session's delegate.
  110. - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
  111. challenges. `nil` by default.
  112. - returns: The new `Manager` instance if the URL session's delegate matches the delegate parameter.
  113. */
  114. public init?(
  115. session: NSURLSession,
  116. delegate: SessionDelegate,
  117. serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
  118. {
  119. self.delegate = delegate
  120. self.session = session
  121. guard delegate === session.delegate else { return nil }
  122. commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
  123. }
  124. private func commonInit(serverTrustPolicyManager serverTrustPolicyManager: ServerTrustPolicyManager?) {
  125. session.serverTrustPolicyManager = serverTrustPolicyManager
  126. delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
  127. guard let strongSelf = self else { return }
  128. dispatch_async(dispatch_get_main_queue()) { strongSelf.backgroundCompletionHandler?() }
  129. }
  130. }
  131. deinit {
  132. session.invalidateAndCancel()
  133. }
  134. // MARK: - Request
  135. /**
  136. Creates a request for the specified method, URL string, parameters, parameter encoding and headers.
  137. - parameter method: The HTTP method.
  138. - parameter URLString: The URL string.
  139. - parameter parameters: The parameters. `nil` by default.
  140. - parameter encoding: The parameter encoding. `.URL` by default.
  141. - parameter headers: The HTTP headers. `nil` by default.
  142. - returns: The created request.
  143. */
  144. public func request(
  145. method: Method,
  146. _ URLString: URLStringConvertible,
  147. parameters: [String: AnyObject]? = nil,
  148. encoding: ParameterEncoding = .URL,
  149. headers: [String: String]? = nil)
  150. -> Request
  151. {
  152. let mutableURLRequest = URLRequest(method, URLString, headers: headers)
  153. let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0
  154. return request(encodedURLRequest)
  155. }
  156. /**
  157. Creates a request for the specified URL request.
  158. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  159. - parameter URLRequest: The URL request
  160. - returns: The created request.
  161. */
  162. public func request(URLRequest: URLRequestConvertible) -> Request {
  163. var dataTask: NSURLSessionDataTask!
  164. dispatch_sync(queue) { dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest) }
  165. let request = Request(session: session, task: dataTask)
  166. delegate[request.delegate.task] = request.delegate
  167. if startRequestsImmediately {
  168. request.resume()
  169. }
  170. return request
  171. }
  172. // MARK: - SessionDelegate
  173. /**
  174. Responsible for handling all delegate callbacks for the underlying session.
  175. */
  176. public final class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
  177. private var subdelegates: [Int: Request.TaskDelegate] = [:]
  178. private let subdelegateQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT)
  179. subscript(task: NSURLSessionTask) -> Request.TaskDelegate? {
  180. get {
  181. var subdelegate: Request.TaskDelegate?
  182. dispatch_sync(subdelegateQueue) { subdelegate = self.subdelegates[task.taskIdentifier] }
  183. return subdelegate
  184. }
  185. set {
  186. dispatch_barrier_async(subdelegateQueue) { self.subdelegates[task.taskIdentifier] = newValue }
  187. }
  188. }
  189. /**
  190. Initializes the `SessionDelegate` instance.
  191. - returns: The new `SessionDelegate` instance.
  192. */
  193. public override init() {
  194. super.init()
  195. }
  196. // MARK: - NSURLSessionDelegate
  197. // MARK: Override Closures
  198. /// Overrides default behavior for NSURLSessionDelegate method `URLSession:didBecomeInvalidWithError:`.
  199. public var sessionDidBecomeInvalidWithError: ((NSURLSession, NSError?) -> Void)?
  200. /// Overrides default behavior for NSURLSessionDelegate method `URLSession:didReceiveChallenge:completionHandler:`.
  201. public var sessionDidReceiveChallenge: ((NSURLSession, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
  202. /// Overrides all behavior for NSURLSessionDelegate method `URLSession:didReceiveChallenge:completionHandler:` and requires the caller to call the `completionHandler`.
  203. public var sessionDidReceiveChallengeWithCompletion: ((NSURLSession, NSURLAuthenticationChallenge, (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) -> Void)?
  204. /// Overrides default behavior for NSURLSessionDelegate method `URLSessionDidFinishEventsForBackgroundURLSession:`.
  205. public var sessionDidFinishEventsForBackgroundURLSession: ((NSURLSession) -> Void)?
  206. // MARK: Delegate Methods
  207. /**
  208. Tells the delegate that the session has been invalidated.
  209. - parameter session: The session object that was invalidated.
  210. - parameter error: The error that caused invalidation, or nil if the invalidation was explicit.
  211. */
  212. public func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
  213. sessionDidBecomeInvalidWithError?(session, error)
  214. }
  215. /**
  216. Requests credentials from the delegate in response to a session-level authentication request from the remote server.
  217. - parameter session: The session containing the task that requested authentication.
  218. - parameter challenge: An object that contains the request for authentication.
  219. - parameter completionHandler: A handler that your delegate method must call providing the disposition and credential.
  220. */
  221. public func URLSession(
  222. session: NSURLSession,
  223. didReceiveChallenge challenge: NSURLAuthenticationChallenge,
  224. completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void))
  225. {
  226. guard sessionDidReceiveChallengeWithCompletion == nil else {
  227. sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
  228. return
  229. }
  230. var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling
  231. var credential: NSURLCredential?
  232. if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
  233. (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
  234. } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
  235. let host = challenge.protectionSpace.host
  236. if let
  237. serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicyForHost(host),
  238. serverTrust = challenge.protectionSpace.serverTrust
  239. {
  240. if serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host) {
  241. disposition = .UseCredential
  242. credential = NSURLCredential(forTrust: serverTrust)
  243. } else {
  244. disposition = .CancelAuthenticationChallenge
  245. }
  246. }
  247. }
  248. completionHandler(disposition, credential)
  249. }
  250. /**
  251. Tells the delegate that all messages enqueued for a session have been delivered.
  252. - parameter session: The session that no longer has any outstanding requests.
  253. */
  254. public func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession) {
  255. sessionDidFinishEventsForBackgroundURLSession?(session)
  256. }
  257. // MARK: - NSURLSessionTaskDelegate
  258. // MARK: Override Closures
  259. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:`.
  260. public var taskWillPerformHTTPRedirection: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest) -> NSURLRequest?)?
  261. /// Overrides all behavior for NSURLSessionTaskDelegate method `URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:` and
  262. /// requires the caller to call the `completionHandler`.
  263. public var taskWillPerformHTTPRedirectionWithCompletion: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest, NSURLRequest? -> Void) -> Void)?
  264. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didReceiveChallenge:completionHandler:`.
  265. public var taskDidReceiveChallenge: ((NSURLSession, NSURLSessionTask, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
  266. /// Overrides all behavior for NSURLSessionTaskDelegate method `URLSession:task:didReceiveChallenge:completionHandler:` and
  267. /// requires the caller to call the `completionHandler`.
  268. public var taskDidReceiveChallengeWithCompletion: ((NSURLSession, NSURLSessionTask, NSURLAuthenticationChallenge, (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) -> Void)?
  269. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:session:task:needNewBodyStream:`.
  270. public var taskNeedNewBodyStream: ((NSURLSession, NSURLSessionTask) -> NSInputStream?)?
  271. /// Overrides all behavior for NSURLSessionTaskDelegate method `URLSession:session:task:needNewBodyStream:` and
  272. /// requires the caller to call the `completionHandler`.
  273. public var taskNeedNewBodyStreamWithCompletion: ((NSURLSession, NSURLSessionTask, NSInputStream? -> Void) -> Void)?
  274. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
  275. public var taskDidSendBodyData: ((NSURLSession, NSURLSessionTask, Int64, Int64, Int64) -> Void)?
  276. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didCompleteWithError:`.
  277. public var taskDidComplete: ((NSURLSession, NSURLSessionTask, NSError?) -> Void)?
  278. // MARK: Delegate Methods
  279. /**
  280. Tells the delegate that the remote server requested an HTTP redirect.
  281. - parameter session: The session containing the task whose request resulted in a redirect.
  282. - parameter task: The task whose request resulted in a redirect.
  283. - parameter response: An object containing the server’s response to the original request.
  284. - parameter request: A URL request object filled out with the new location.
  285. - parameter completionHandler: A closure that your handler should call with either the value of the request
  286. parameter, a modified URL request object, or NULL to refuse the redirect and
  287. return the body of the redirect response.
  288. */
  289. public func URLSession(
  290. session: NSURLSession,
  291. task: NSURLSessionTask,
  292. willPerformHTTPRedirection response: NSHTTPURLResponse,
  293. newRequest request: NSURLRequest,
  294. completionHandler: NSURLRequest? -> Void)
  295. {
  296. guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
  297. taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
  298. return
  299. }
  300. var redirectRequest: NSURLRequest? = request
  301. if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
  302. redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
  303. }
  304. completionHandler(redirectRequest)
  305. }
  306. /**
  307. Requests credentials from the delegate in response to an authentication request from the remote server.
  308. - parameter session: The session containing the task whose request requires authentication.
  309. - parameter task: The task whose request requires authentication.
  310. - parameter challenge: An object that contains the request for authentication.
  311. - parameter completionHandler: A handler that your delegate method must call providing the disposition and credential.
  312. */
  313. public func URLSession(
  314. session: NSURLSession,
  315. task: NSURLSessionTask,
  316. didReceiveChallenge challenge: NSURLAuthenticationChallenge,
  317. completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)
  318. {
  319. guard taskDidReceiveChallengeWithCompletion == nil else {
  320. taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
  321. return
  322. }
  323. if let taskDidReceiveChallenge = taskDidReceiveChallenge {
  324. let result = taskDidReceiveChallenge(session, task, challenge)
  325. completionHandler(result.0, result.1)
  326. } else if let delegate = self[task] {
  327. delegate.URLSession(
  328. session,
  329. task: task,
  330. didReceiveChallenge: challenge,
  331. completionHandler: completionHandler
  332. )
  333. } else {
  334. URLSession(session, didReceiveChallenge: challenge, completionHandler: completionHandler)
  335. }
  336. }
  337. /**
  338. Tells the delegate when a task requires a new request body stream to send to the remote server.
  339. - parameter session: The session containing the task that needs a new body stream.
  340. - parameter task: The task that needs a new body stream.
  341. - parameter completionHandler: A completion handler that your delegate method should call with the new body stream.
  342. */
  343. public func URLSession(
  344. session: NSURLSession,
  345. task: NSURLSessionTask,
  346. needNewBodyStream completionHandler: NSInputStream? -> Void)
  347. {
  348. guard taskNeedNewBodyStreamWithCompletion == nil else {
  349. taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
  350. return
  351. }
  352. if let taskNeedNewBodyStream = taskNeedNewBodyStream {
  353. completionHandler(taskNeedNewBodyStream(session, task))
  354. } else if let delegate = self[task] {
  355. delegate.URLSession(session, task: task, needNewBodyStream: completionHandler)
  356. }
  357. }
  358. /**
  359. Periodically informs the delegate of the progress of sending body content to the server.
  360. - parameter session: The session containing the data task.
  361. - parameter task: The data task.
  362. - parameter bytesSent: The number of bytes sent since the last time this delegate method was called.
  363. - parameter totalBytesSent: The total number of bytes sent so far.
  364. - parameter totalBytesExpectedToSend: The expected length of the body data.
  365. */
  366. public func URLSession(
  367. session: NSURLSession,
  368. task: NSURLSessionTask,
  369. didSendBodyData bytesSent: Int64,
  370. totalBytesSent: Int64,
  371. totalBytesExpectedToSend: Int64)
  372. {
  373. if let taskDidSendBodyData = taskDidSendBodyData {
  374. taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
  375. } else if let delegate = self[task] as? Request.UploadTaskDelegate {
  376. delegate.URLSession(
  377. session,
  378. task: task,
  379. didSendBodyData: bytesSent,
  380. totalBytesSent: totalBytesSent,
  381. totalBytesExpectedToSend: totalBytesExpectedToSend
  382. )
  383. }
  384. }
  385. /**
  386. Tells the delegate that the task finished transferring data.
  387. - parameter session: The session containing the task whose request finished transferring data.
  388. - parameter task: The task whose request finished transferring data.
  389. - parameter error: If an error occurred, an error object indicating how the transfer failed, otherwise nil.
  390. */
  391. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
  392. if let taskDidComplete = taskDidComplete {
  393. taskDidComplete(session, task, error)
  394. } else if let delegate = self[task] {
  395. delegate.URLSession(session, task: task, didCompleteWithError: error)
  396. }
  397. NSNotificationCenter.defaultCenter().postNotificationName(Notifications.Task.DidComplete, object: task)
  398. self[task] = nil
  399. }
  400. // MARK: - NSURLSessionDataDelegate
  401. // MARK: Override Closures
  402. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveResponse:completionHandler:`.
  403. public var dataTaskDidReceiveResponse: ((NSURLSession, NSURLSessionDataTask, NSURLResponse) -> NSURLSessionResponseDisposition)?
  404. /// Overrides all behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveResponse:completionHandler:` and
  405. /// requires caller to call the `completionHandler`.
  406. public var dataTaskDidReceiveResponseWithCompletion: ((NSURLSession, NSURLSessionDataTask, NSURLResponse, NSURLSessionResponseDisposition -> Void) -> Void)?
  407. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didBecomeDownloadTask:`.
  408. public var dataTaskDidBecomeDownloadTask: ((NSURLSession, NSURLSessionDataTask, NSURLSessionDownloadTask) -> Void)?
  409. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveData:`.
  410. public var dataTaskDidReceiveData: ((NSURLSession, NSURLSessionDataTask, NSData) -> Void)?
  411. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:willCacheResponse:completionHandler:`.
  412. public var dataTaskWillCacheResponse: ((NSURLSession, NSURLSessionDataTask, NSCachedURLResponse) -> NSCachedURLResponse?)?
  413. /// Overrides all behavior for NSURLSessionDataDelegate method `URLSession:dataTask:willCacheResponse:completionHandler:` and
  414. /// requires caller to call the `completionHandler`.
  415. public var dataTaskWillCacheResponseWithCompletion: ((NSURLSession, NSURLSessionDataTask, NSCachedURLResponse, NSCachedURLResponse? -> Void) -> Void)?
  416. // MARK: Delegate Methods
  417. /**
  418. Tells the delegate that the data task received the initial reply (headers) from the server.
  419. - parameter session: The session containing the data task that received an initial reply.
  420. - parameter dataTask: The data task that received an initial reply.
  421. - parameter response: A URL response object populated with headers.
  422. - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a
  423. constant to indicate whether the transfer should continue as a data task or
  424. should become a download task.
  425. */
  426. public func URLSession(
  427. session: NSURLSession,
  428. dataTask: NSURLSessionDataTask,
  429. didReceiveResponse response: NSURLResponse,
  430. completionHandler: NSURLSessionResponseDisposition -> Void)
  431. {
  432. guard dataTaskDidReceiveResponseWithCompletion == nil else {
  433. dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
  434. return
  435. }
  436. var disposition: NSURLSessionResponseDisposition = .Allow
  437. if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
  438. disposition = dataTaskDidReceiveResponse(session, dataTask, response)
  439. }
  440. completionHandler(disposition)
  441. }
  442. /**
  443. Tells the delegate that the data task was changed to a download task.
  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. */
  448. public func URLSession(
  449. session: NSURLSession,
  450. dataTask: NSURLSessionDataTask,
  451. didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask)
  452. {
  453. if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
  454. dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
  455. } else {
  456. let downloadDelegate = Request.DownloadTaskDelegate(task: downloadTask)
  457. self[downloadTask] = downloadDelegate
  458. }
  459. }
  460. /**
  461. Tells the delegate that the data task has received some of the expected data.
  462. - parameter session: The session containing the data task that provided data.
  463. - parameter dataTask: The data task that provided data.
  464. - parameter data: A data object containing the transferred data.
  465. */
  466. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
  467. if let dataTaskDidReceiveData = dataTaskDidReceiveData {
  468. dataTaskDidReceiveData(session, dataTask, data)
  469. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  470. delegate.URLSession(session, dataTask: dataTask, didReceiveData: data)
  471. }
  472. }
  473. /**
  474. Asks the delegate whether the data (or upload) task should store the response in the cache.
  475. - parameter session: The session containing the data (or upload) task.
  476. - parameter dataTask: The data (or upload) task.
  477. - parameter proposedResponse: The default caching behavior. This behavior is determined based on the current
  478. caching policy and the values of certain received headers, such as the Pragma
  479. and Cache-Control headers.
  480. - parameter completionHandler: A block that your handler must call, providing either the original proposed
  481. response, a modified version of that response, or NULL to prevent caching the
  482. response. If your delegate implements this method, it must call this completion
  483. handler; otherwise, your app leaks memory.
  484. */
  485. public func URLSession(
  486. session: NSURLSession,
  487. dataTask: NSURLSessionDataTask,
  488. willCacheResponse proposedResponse: NSCachedURLResponse,
  489. completionHandler: NSCachedURLResponse? -> Void)
  490. {
  491. guard dataTaskWillCacheResponseWithCompletion == nil else {
  492. dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
  493. return
  494. }
  495. if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
  496. completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
  497. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  498. delegate.URLSession(
  499. session,
  500. dataTask: dataTask,
  501. willCacheResponse: proposedResponse,
  502. completionHandler: completionHandler
  503. )
  504. } else {
  505. completionHandler(proposedResponse)
  506. }
  507. }
  508. // MARK: - NSURLSessionDownloadDelegate
  509. // MARK: Override Closures
  510. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didFinishDownloadingToURL:`.
  511. public var downloadTaskDidFinishDownloadingToURL: ((NSURLSession, NSURLSessionDownloadTask, NSURL) -> Void)?
  512. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`.
  513. public var downloadTaskDidWriteData: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
  514. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`.
  515. public var downloadTaskDidResumeAtOffset: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64) -> Void)?
  516. // MARK: Delegate Methods
  517. /**
  518. Tells the delegate that a download task has finished downloading.
  519. - parameter session: The session containing the download task that finished.
  520. - parameter downloadTask: The download task that finished.
  521. - parameter location: A file URL for the temporary file. Because the file is temporary, you must either
  522. open the file for reading or move it to a permanent location in your app’s sandbox
  523. container directory before returning from this delegate method.
  524. */
  525. public func URLSession(
  526. session: NSURLSession,
  527. downloadTask: NSURLSessionDownloadTask,
  528. didFinishDownloadingToURL location: NSURL)
  529. {
  530. if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
  531. downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
  532. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  533. delegate.URLSession(session, downloadTask: downloadTask, didFinishDownloadingToURL: location)
  534. }
  535. }
  536. /**
  537. Periodically informs the delegate about the download’s progress.
  538. - parameter session: The session containing the download task.
  539. - parameter downloadTask: The download task.
  540. - parameter bytesWritten: The number of bytes transferred since the last time this delegate
  541. method was called.
  542. - parameter totalBytesWritten: The total number of bytes transferred so far.
  543. - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length
  544. header. If this header was not provided, the value is
  545. `NSURLSessionTransferSizeUnknown`.
  546. */
  547. public func URLSession(
  548. session: NSURLSession,
  549. downloadTask: NSURLSessionDownloadTask,
  550. didWriteData bytesWritten: Int64,
  551. totalBytesWritten: Int64,
  552. totalBytesExpectedToWrite: Int64)
  553. {
  554. if let downloadTaskDidWriteData = downloadTaskDidWriteData {
  555. downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
  556. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  557. delegate.URLSession(
  558. session,
  559. downloadTask: downloadTask,
  560. didWriteData: bytesWritten,
  561. totalBytesWritten: totalBytesWritten,
  562. totalBytesExpectedToWrite: totalBytesExpectedToWrite
  563. )
  564. }
  565. }
  566. /**
  567. Tells the delegate that the download task has resumed downloading.
  568. - parameter session: The session containing the download task that finished.
  569. - parameter downloadTask: The download task that resumed. See explanation in the discussion.
  570. - parameter fileOffset: If the file's cache policy or last modified date prevents reuse of the
  571. existing content, then this value is zero. Otherwise, this value is an
  572. integer representing the number of bytes on disk that do not need to be
  573. retrieved again.
  574. - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header.
  575. If this header was not provided, the value is NSURLSessionTransferSizeUnknown.
  576. */
  577. public func URLSession(
  578. session: NSURLSession,
  579. downloadTask: NSURLSessionDownloadTask,
  580. didResumeAtOffset fileOffset: Int64,
  581. expectedTotalBytes: Int64)
  582. {
  583. if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
  584. downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
  585. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  586. delegate.URLSession(
  587. session,
  588. downloadTask: downloadTask,
  589. didResumeAtOffset: fileOffset,
  590. expectedTotalBytes: expectedTotalBytes
  591. )
  592. }
  593. }
  594. // MARK: - NSURLSessionStreamDelegate
  595. var _streamTaskReadClosed: Any?
  596. var _streamTaskWriteClosed: Any?
  597. var _streamTaskBetterRouteDiscovered: Any?
  598. var _streamTaskDidBecomeInputStream: Any?
  599. // MARK: - NSObject
  600. public override func respondsToSelector(selector: Selector) -> Bool {
  601. #if !os(OSX)
  602. if selector == #selector(NSURLSessionDelegate.URLSessionDidFinishEventsForBackgroundURLSession(_:)) {
  603. return sessionDidFinishEventsForBackgroundURLSession != nil
  604. }
  605. #endif
  606. switch selector {
  607. case #selector(NSURLSessionDelegate.URLSession(_:didBecomeInvalidWithError:)):
  608. return sessionDidBecomeInvalidWithError != nil
  609. case #selector(NSURLSessionDelegate.URLSession(_:didReceiveChallenge:completionHandler:)):
  610. return (sessionDidReceiveChallenge != nil || sessionDidReceiveChallengeWithCompletion != nil)
  611. case #selector(NSURLSessionTaskDelegate.URLSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
  612. return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
  613. case #selector(NSURLSessionDataDelegate.URLSession(_:dataTask:didReceiveResponse:completionHandler:)):
  614. return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
  615. default:
  616. return self.dynamicType.instancesRespondToSelector(selector)
  617. }
  618. }
  619. }
  620. }