Manager.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. // Manager.swift
  2. //
  3. // Copyright (c) 2014–2015 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: String = {
  45. var components: [String] = []
  46. for (index, languageCode) in (NSLocale.preferredLanguages() as [String]).enumerate() {
  47. let q = 1.0 - (Double(index) * 0.1)
  48. components.append("\(languageCode);q=\(q)")
  49. if q <= 0.5 {
  50. break
  51. }
  52. }
  53. return ",".join(components)
  54. }()
  55. // User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
  56. let userAgent: String = {
  57. if let info = NSBundle.mainBundle().infoDictionary {
  58. let executable: AnyObject = info[kCFBundleExecutableKey as String] ?? "Unknown"
  59. let bundle: AnyObject = info[kCFBundleIdentifierKey as String] ?? "Unknown"
  60. let version: AnyObject = info[kCFBundleVersionKey as String] ?? "Unknown"
  61. let os: AnyObject = NSProcessInfo.processInfo().operatingSystemVersionString ?? "Unknown"
  62. var mutableUserAgent = NSMutableString(string: "\(executable)/\(bundle) (\(version); OS \(os))") as CFMutableString
  63. let transform = NSString(string: "Any-Latin; Latin-ASCII; [:^ASCII:] Remove") as CFString
  64. if CFStringTransform(mutableUserAgent, UnsafeMutablePointer<CFRange>(nil), transform, false) {
  65. return mutableUserAgent as String
  66. }
  67. }
  68. return "Alamofire"
  69. }()
  70. return [
  71. "Accept-Encoding": acceptEncoding,
  72. "Accept-Language": acceptLanguage,
  73. "User-Agent": userAgent
  74. ]
  75. }()
  76. let queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
  77. /// The underlying session.
  78. public let session: NSURLSession
  79. /// The session delegate handling all the task and session delegate callbacks.
  80. public let delegate: SessionDelegate
  81. /// Whether to start requests immediately after being constructed. `true` by default.
  82. public var startRequestsImmediately: Bool = true
  83. /**
  84. The background completion handler closure provided by the UIApplicationDelegate
  85. `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background
  86. completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation
  87. will automatically call the handler.
  88. If you need to handle your own events before the handler is called, then you need to override the
  89. SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished.
  90. `nil` by default.
  91. */
  92. public var backgroundCompletionHandler: (() -> Void)?
  93. // MARK: - Lifecycle
  94. /**
  95. Initializes the `Manager` instance with the given configuration and server trust policy.
  96. - parameter configuration: The configuration used to construct the managed session.
  97. `NSURLSessionConfiguration.defaultSessionConfiguration()` by default.
  98. - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
  99. challenges. `nil` by default.
  100. - returns: The new `Manager` instance.
  101. */
  102. required public init(
  103. configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration(),
  104. serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
  105. {
  106. self.delegate = SessionDelegate()
  107. self.session = NSURLSession(configuration: configuration, delegate: self.delegate, delegateQueue: nil)
  108. self.session.serverTrustPolicyManager = serverTrustPolicyManager
  109. self.delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
  110. if let strongSelf = self {
  111. strongSelf.backgroundCompletionHandler?()
  112. }
  113. }
  114. }
  115. deinit {
  116. session.invalidateAndCancel()
  117. }
  118. // MARK: - Request
  119. /**
  120. Creates a request for the specified method, URL string, parameters, and parameter encoding.
  121. - parameter method: The HTTP method.
  122. - parameter URLString: The URL string.
  123. - parameter parameters: The parameters. `nil` by default.
  124. - parameter encoding: The parameter encoding. `.URL` by default.
  125. - parameter headers: The HTTP headers. `nil` by default.
  126. - returns: The created request.
  127. */
  128. public func request(
  129. method: Method,
  130. _ URLString: URLStringConvertible,
  131. parameters: [String: AnyObject]? = nil,
  132. encoding: ParameterEncoding = .URL,
  133. headers: [String: String]? = nil)
  134. -> Request
  135. {
  136. let mutableURLRequest = URLRequest(method, URLString, headers: headers)
  137. let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0
  138. return request(encodedURLRequest)
  139. }
  140. /**
  141. Creates a request for the specified URL request.
  142. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  143. - parameter URLRequest: The URL request
  144. - returns: The created request.
  145. */
  146. public func request(URLRequest: URLRequestConvertible) -> Request {
  147. var dataTask: NSURLSessionDataTask!
  148. dispatch_sync(queue) {
  149. dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest)
  150. }
  151. let request = Request(session: session, task: dataTask)
  152. delegate[request.delegate.task] = request.delegate
  153. if startRequestsImmediately {
  154. request.resume()
  155. }
  156. return request
  157. }
  158. // MARK: - SessionDelegate
  159. /**
  160. Responsible for handling all delegate callbacks for the underlying session.
  161. */
  162. public final class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
  163. private var subdelegates: [Int: Request.TaskDelegate] = [:]
  164. private let subdelegateQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT)
  165. subscript(task: NSURLSessionTask) -> Request.TaskDelegate? {
  166. get {
  167. var subdelegate: Request.TaskDelegate?
  168. dispatch_sync(subdelegateQueue) {
  169. subdelegate = self.subdelegates[task.taskIdentifier]
  170. }
  171. return subdelegate
  172. }
  173. set {
  174. dispatch_barrier_async(subdelegateQueue) {
  175. self.subdelegates[task.taskIdentifier] = newValue
  176. }
  177. }
  178. }
  179. // MARK: - NSURLSessionDelegate
  180. // MARK: Override Closures
  181. /// NSURLSessionDelegate override closure for `URLSession:didBecomeInvalidWithError:` method.
  182. public var sessionDidBecomeInvalidWithError: ((NSURLSession, NSError?) -> Void)?
  183. /// NSURLSessionDelegate override closure for `URLSession:didReceiveChallenge:completionHandler:` method.
  184. public var sessionDidReceiveChallenge: ((NSURLSession, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
  185. /// NSURLSessionDelegate override closure for `URLSessionDidFinishEventsForBackgroundURLSession:` method.
  186. public var sessionDidFinishEventsForBackgroundURLSession: ((NSURLSession) -> Void)?
  187. // MARK: Delegate Methods
  188. public func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
  189. sessionDidBecomeInvalidWithError?(session, error)
  190. }
  191. public func URLSession(
  192. session: NSURLSession,
  193. didReceiveChallenge challenge: NSURLAuthenticationChallenge,
  194. completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void))
  195. {
  196. var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling
  197. var credential: NSURLCredential?
  198. if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
  199. (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
  200. } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
  201. let host = challenge.protectionSpace.host
  202. if let
  203. serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicyForHost(host),
  204. serverTrust = challenge.protectionSpace.serverTrust
  205. {
  206. if serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host) {
  207. disposition = .UseCredential
  208. credential = NSURLCredential(forTrust: serverTrust)
  209. } else {
  210. disposition = .CancelAuthenticationChallenge
  211. }
  212. }
  213. }
  214. completionHandler(disposition, credential)
  215. }
  216. public func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession) {
  217. sessionDidFinishEventsForBackgroundURLSession?(session)
  218. }
  219. // MARK: - NSURLSessionTaskDelegate
  220. // MARK: Override Closures
  221. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:`.
  222. public var taskWillPerformHTTPRedirection: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest) -> NSURLRequest?)?
  223. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didReceiveChallenge:completionHandler:`.
  224. public var taskDidReceiveChallenge: ((NSURLSession, NSURLSessionTask, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
  225. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:session:task:needNewBodyStream:`.
  226. public var taskNeedNewBodyStream: ((NSURLSession, NSURLSessionTask) -> NSInputStream!)?
  227. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
  228. public var taskDidSendBodyData: ((NSURLSession, NSURLSessionTask, Int64, Int64, Int64) -> Void)?
  229. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didCompleteWithError:`.
  230. public var taskDidComplete: ((NSURLSession, NSURLSessionTask, NSError?) -> Void)?
  231. // MARK: Delegate Methods
  232. public func URLSession(
  233. session: NSURLSession,
  234. task: NSURLSessionTask,
  235. willPerformHTTPRedirection response: NSHTTPURLResponse,
  236. newRequest request: NSURLRequest,
  237. completionHandler: ((NSURLRequest?) -> Void))
  238. {
  239. var redirectRequest: NSURLRequest? = request
  240. if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
  241. redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
  242. }
  243. completionHandler(redirectRequest)
  244. }
  245. public func URLSession(
  246. session: NSURLSession,
  247. task: NSURLSessionTask,
  248. didReceiveChallenge challenge: NSURLAuthenticationChallenge,
  249. completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void))
  250. {
  251. if let taskDidReceiveChallenge = taskDidReceiveChallenge {
  252. completionHandler(taskDidReceiveChallenge(session, task, challenge))
  253. } else if let delegate = self[task] {
  254. delegate.URLSession(
  255. session,
  256. task: task,
  257. didReceiveChallenge: challenge,
  258. completionHandler: completionHandler
  259. )
  260. } else {
  261. URLSession(session, didReceiveChallenge: challenge, completionHandler: completionHandler)
  262. }
  263. }
  264. public func URLSession(
  265. session: NSURLSession,
  266. task: NSURLSessionTask,
  267. needNewBodyStream completionHandler: ((NSInputStream?) -> Void))
  268. {
  269. if let taskNeedNewBodyStream = taskNeedNewBodyStream {
  270. completionHandler(taskNeedNewBodyStream(session, task))
  271. } else if let delegate = self[task] {
  272. delegate.URLSession(session, task: task, needNewBodyStream: completionHandler)
  273. }
  274. }
  275. public func URLSession(
  276. session: NSURLSession,
  277. task: NSURLSessionTask,
  278. didSendBodyData bytesSent: Int64,
  279. totalBytesSent: Int64,
  280. totalBytesExpectedToSend: Int64)
  281. {
  282. if let taskDidSendBodyData = taskDidSendBodyData {
  283. taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
  284. } else if let delegate = self[task] as? Request.UploadTaskDelegate {
  285. delegate.URLSession(
  286. session,
  287. task: task,
  288. didSendBodyData: bytesSent,
  289. totalBytesSent: totalBytesSent,
  290. totalBytesExpectedToSend: totalBytesExpectedToSend
  291. )
  292. }
  293. }
  294. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
  295. if let taskDidComplete = taskDidComplete {
  296. taskDidComplete(session, task, error)
  297. } else if let delegate = self[task] {
  298. delegate.URLSession(session, task: task, didCompleteWithError: error)
  299. }
  300. self[task] = nil
  301. }
  302. // MARK: - NSURLSessionDataDelegate
  303. // MARK: Override Closures
  304. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveResponse:completionHandler:`.
  305. public var dataTaskDidReceiveResponse: ((NSURLSession, NSURLSessionDataTask, NSURLResponse) -> NSURLSessionResponseDisposition)?
  306. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didBecomeDownloadTask:`.
  307. public var dataTaskDidBecomeDownloadTask: ((NSURLSession, NSURLSessionDataTask, NSURLSessionDownloadTask) -> Void)?
  308. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveData:`.
  309. public var dataTaskDidReceiveData: ((NSURLSession, NSURLSessionDataTask, NSData) -> Void)?
  310. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:willCacheResponse:completionHandler:`.
  311. public var dataTaskWillCacheResponse: ((NSURLSession, NSURLSessionDataTask, NSCachedURLResponse) -> NSCachedURLResponse!)?
  312. // MARK: Delegate Methods
  313. public func URLSession(
  314. session: NSURLSession,
  315. dataTask: NSURLSessionDataTask,
  316. didReceiveResponse response: NSURLResponse,
  317. completionHandler: ((NSURLSessionResponseDisposition) -> Void))
  318. {
  319. var disposition: NSURLSessionResponseDisposition = .Allow
  320. if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
  321. disposition = dataTaskDidReceiveResponse(session, dataTask, response)
  322. }
  323. completionHandler(disposition)
  324. }
  325. public func URLSession(
  326. session: NSURLSession,
  327. dataTask: NSURLSessionDataTask,
  328. didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask)
  329. {
  330. if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
  331. dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
  332. } else {
  333. let downloadDelegate = Request.DownloadTaskDelegate(task: downloadTask)
  334. self[downloadTask] = downloadDelegate
  335. }
  336. }
  337. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
  338. if let dataTaskDidReceiveData = dataTaskDidReceiveData {
  339. dataTaskDidReceiveData(session, dataTask, data)
  340. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  341. delegate.URLSession(session, dataTask: dataTask, didReceiveData: data)
  342. }
  343. }
  344. public func URLSession(
  345. session: NSURLSession,
  346. dataTask: NSURLSessionDataTask,
  347. willCacheResponse proposedResponse: NSCachedURLResponse,
  348. completionHandler: ((NSCachedURLResponse?) -> Void))
  349. {
  350. if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
  351. completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
  352. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  353. delegate.URLSession(
  354. session,
  355. dataTask: dataTask,
  356. willCacheResponse: proposedResponse,
  357. completionHandler: completionHandler
  358. )
  359. } else {
  360. completionHandler(proposedResponse)
  361. }
  362. }
  363. // MARK: - NSURLSessionDownloadDelegate
  364. // MARK: Override Closures
  365. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didFinishDownloadingToURL:`.
  366. public var downloadTaskDidFinishDownloadingToURL: ((NSURLSession, NSURLSessionDownloadTask, NSURL) -> Void)?
  367. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`.
  368. public var downloadTaskDidWriteData: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
  369. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`.
  370. public var downloadTaskDidResumeAtOffset: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64) -> Void)?
  371. // MARK: Delegate Methods
  372. public func URLSession(
  373. session: NSURLSession,
  374. downloadTask: NSURLSessionDownloadTask,
  375. didFinishDownloadingToURL location: NSURL)
  376. {
  377. if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
  378. downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
  379. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  380. delegate.URLSession(session, downloadTask: downloadTask, didFinishDownloadingToURL: location)
  381. }
  382. }
  383. public func URLSession(
  384. session: NSURLSession,
  385. downloadTask: NSURLSessionDownloadTask,
  386. didWriteData bytesWritten: Int64,
  387. totalBytesWritten: Int64,
  388. totalBytesExpectedToWrite: Int64)
  389. {
  390. if let downloadTaskDidWriteData = downloadTaskDidWriteData {
  391. downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
  392. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  393. delegate.URLSession(
  394. session,
  395. downloadTask: downloadTask,
  396. didWriteData: bytesWritten,
  397. totalBytesWritten: totalBytesWritten,
  398. totalBytesExpectedToWrite: totalBytesExpectedToWrite
  399. )
  400. }
  401. }
  402. public func URLSession(
  403. session: NSURLSession,
  404. downloadTask: NSURLSessionDownloadTask,
  405. didResumeAtOffset fileOffset: Int64,
  406. expectedTotalBytes: Int64)
  407. {
  408. if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
  409. downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
  410. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  411. delegate.URLSession(
  412. session,
  413. downloadTask: downloadTask,
  414. didResumeAtOffset: fileOffset,
  415. expectedTotalBytes: expectedTotalBytes
  416. )
  417. }
  418. }
  419. }
  420. }