Manager.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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 for any ad hoc requests.
  30. */
  31. public static let sharedInstance: Manager = {
  32. let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
  33. configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
  34. return Manager(configuration: configuration)
  35. }()
  36. /**
  37. Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers.
  38. - returns: The default header values.
  39. */
  40. public static let defaultHTTPHeaders: [String: String] = {
  41. // Accept-Encoding HTTP Header; see http://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 http://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 http://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, nil, transform, 0) == 1 {
  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. /// The background completion handler closure provided by the UIApplicationDelegate `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation will automatically call the handler. If you need to handle your own events before the handler is called, then you need to override the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished. `nil` by default.
  84. public var backgroundCompletionHandler: (() -> Void)?
  85. // MARK: - Lifecycle
  86. /**
  87. Initializes the `Manager` instance with the given configuration and server trust policy.
  88. - parameter configuration: The configuration used to construct the managed session. `NSURLSessionConfiguration.defaultSessionConfiguration()` by default.
  89. - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust challenges. `nil` by default.
  90. - returns: The new `Manager` instance.
  91. */
  92. required public init(
  93. configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration(),
  94. serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
  95. {
  96. self.delegate = SessionDelegate()
  97. self.session = NSURLSession(configuration: configuration, delegate: self.delegate, delegateQueue: nil)
  98. self.session.serverTrustPolicyManager = serverTrustPolicyManager
  99. self.delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
  100. if let strongSelf = self {
  101. strongSelf.backgroundCompletionHandler?()
  102. }
  103. }
  104. }
  105. deinit {
  106. session.invalidateAndCancel()
  107. }
  108. // MARK: - Request
  109. /**
  110. Creates a request for the specified method, URL string, parameters, and parameter encoding.
  111. - parameter method: The HTTP method.
  112. - parameter URLString: The URL string.
  113. - parameter parameters: The parameters. `nil` by default.
  114. - parameter encoding: The parameter encoding. `.URL` by default.
  115. - parameter headers: The HTTP headers. `nil` by default.
  116. - returns: The created request.
  117. */
  118. public func request(
  119. method: Method,
  120. _ URLString: URLStringConvertible,
  121. parameters: [String: AnyObject]? = nil,
  122. encoding: ParameterEncoding = .URL,
  123. headers: [String: String]? = nil)
  124. -> Request
  125. {
  126. let mutableURLRequest = URLRequest(method, URLString, headers: headers)
  127. let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0
  128. return request(encodedURLRequest)
  129. }
  130. /**
  131. Creates a request for the specified URL request.
  132. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  133. - parameter URLRequest: The URL request
  134. - returns: The created request.
  135. */
  136. public func request(URLRequest: URLRequestConvertible) -> Request {
  137. var dataTask: NSURLSessionDataTask!
  138. dispatch_sync(queue) {
  139. dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest)
  140. }
  141. let request = Request(session: session, task: dataTask)
  142. delegate[request.delegate.task] = request.delegate
  143. if startRequestsImmediately {
  144. request.resume()
  145. }
  146. return request
  147. }
  148. // MARK: - SessionDelegate
  149. /**
  150. Responsible for handling all delegate callbacks for the underlying session.
  151. */
  152. public final class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
  153. private var subdelegates: [Int: Request.TaskDelegate] = [:]
  154. private let subdelegateQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT)
  155. subscript(task: NSURLSessionTask) -> Request.TaskDelegate? {
  156. get {
  157. var subdelegate: Request.TaskDelegate?
  158. dispatch_sync(subdelegateQueue) {
  159. subdelegate = self.subdelegates[task.taskIdentifier]
  160. }
  161. return subdelegate
  162. }
  163. set {
  164. dispatch_barrier_async(subdelegateQueue) {
  165. self.subdelegates[task.taskIdentifier] = newValue
  166. }
  167. }
  168. }
  169. // MARK: - NSURLSessionDelegate
  170. // MARK: Override Closures
  171. /// NSURLSessionDelegate override closure for `URLSession:didBecomeInvalidWithError:` method.
  172. public var sessionDidBecomeInvalidWithError: ((NSURLSession, NSError?) -> Void)?
  173. /// NSURLSessionDelegate override closure for `URLSession:didReceiveChallenge:completionHandler:` method.
  174. public var sessionDidReceiveChallenge: ((NSURLSession, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
  175. /// NSURLSessionDelegate override closure for `URLSessionDidFinishEventsForBackgroundURLSession:` method.
  176. public var sessionDidFinishEventsForBackgroundURLSession: ((NSURLSession) -> Void)?
  177. // MARK: Delegate Methods
  178. public func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
  179. sessionDidBecomeInvalidWithError?(session, error)
  180. }
  181. public func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)) {
  182. var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling
  183. var credential: NSURLCredential?
  184. if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
  185. (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
  186. } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
  187. let host = challenge.protectionSpace.host
  188. if let
  189. serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicyForHost(host),
  190. serverTrust = challenge.protectionSpace.serverTrust
  191. {
  192. if serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host) {
  193. disposition = .UseCredential
  194. credential = NSURLCredential(forTrust: serverTrust)
  195. } else {
  196. disposition = .CancelAuthenticationChallenge
  197. }
  198. }
  199. }
  200. completionHandler(disposition, credential)
  201. }
  202. public func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession) {
  203. sessionDidFinishEventsForBackgroundURLSession?(session)
  204. }
  205. // MARK: - NSURLSessionTaskDelegate
  206. // MARK: Override Closures
  207. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:`.
  208. public var taskWillPerformHTTPRedirection: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest) -> NSURLRequest?)?
  209. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didReceiveChallenge:completionHandler:`.
  210. public var taskDidReceiveChallenge: ((NSURLSession, NSURLSessionTask, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
  211. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:session:task:needNewBodyStream:`.
  212. public var taskNeedNewBodyStream: ((NSURLSession, NSURLSessionTask) -> NSInputStream!)?
  213. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
  214. public var taskDidSendBodyData: ((NSURLSession, NSURLSessionTask, Int64, Int64, Int64) -> Void)?
  215. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didCompleteWithError:`.
  216. public var taskDidComplete: ((NSURLSession, NSURLSessionTask, NSError?) -> Void)?
  217. // MARK: Delegate Methods
  218. public func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: ((NSURLRequest?) -> Void)) {
  219. var redirectRequest: NSURLRequest? = request
  220. if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
  221. redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
  222. }
  223. completionHandler(redirectRequest)
  224. }
  225. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)) {
  226. if let taskDidReceiveChallenge = taskDidReceiveChallenge {
  227. completionHandler(taskDidReceiveChallenge(session, task, challenge))
  228. } else if let delegate = self[task] {
  229. delegate.URLSession(session, task: task, didReceiveChallenge: challenge, completionHandler: completionHandler)
  230. } else {
  231. URLSession(session, didReceiveChallenge: challenge, completionHandler: completionHandler)
  232. }
  233. }
  234. public func URLSession(session: NSURLSession, task: NSURLSessionTask, needNewBodyStream completionHandler: ((NSInputStream?) -> Void)) {
  235. if let taskNeedNewBodyStream = taskNeedNewBodyStream {
  236. completionHandler(taskNeedNewBodyStream(session, task))
  237. } else if let delegate = self[task] {
  238. delegate.URLSession(session, task: task, needNewBodyStream: completionHandler)
  239. }
  240. }
  241. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
  242. if let taskDidSendBodyData = taskDidSendBodyData {
  243. taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
  244. } else if let delegate = self[task] as? Request.UploadTaskDelegate {
  245. delegate.URLSession(session, task: task, didSendBodyData: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend)
  246. }
  247. }
  248. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
  249. if let taskDidComplete = taskDidComplete {
  250. taskDidComplete(session, task, error)
  251. } else if let delegate = self[task] {
  252. delegate.URLSession(session, task: task, didCompleteWithError: error)
  253. self[task] = nil
  254. }
  255. }
  256. // MARK: - NSURLSessionDataDelegate
  257. // MARK: Override Closures
  258. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveResponse:completionHandler:`.
  259. public var dataTaskDidReceiveResponse: ((NSURLSession, NSURLSessionDataTask, NSURLResponse) -> NSURLSessionResponseDisposition)?
  260. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didBecomeDownloadTask:`.
  261. public var dataTaskDidBecomeDownloadTask: ((NSURLSession, NSURLSessionDataTask, NSURLSessionDownloadTask) -> Void)?
  262. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveData:`.
  263. public var dataTaskDidReceiveData: ((NSURLSession, NSURLSessionDataTask, NSData) -> Void)?
  264. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:willCacheResponse:completionHandler:`.
  265. public var dataTaskWillCacheResponse: ((NSURLSession, NSURLSessionDataTask, NSCachedURLResponse) -> NSCachedURLResponse!)?
  266. // MARK: Delegate Methods
  267. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: ((NSURLSessionResponseDisposition) -> Void)) {
  268. var disposition: NSURLSessionResponseDisposition = .Allow
  269. if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
  270. disposition = dataTaskDidReceiveResponse(session, dataTask, response)
  271. }
  272. completionHandler(disposition)
  273. }
  274. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask) {
  275. if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
  276. dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
  277. } else {
  278. let downloadDelegate = Request.DownloadTaskDelegate(task: downloadTask)
  279. self[downloadTask] = downloadDelegate
  280. }
  281. }
  282. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
  283. if let dataTaskDidReceiveData = dataTaskDidReceiveData {
  284. dataTaskDidReceiveData(session, dataTask, data)
  285. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  286. delegate.URLSession(session, dataTask: dataTask, didReceiveData: data)
  287. }
  288. }
  289. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: ((NSCachedURLResponse?) -> Void)) {
  290. if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
  291. completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
  292. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  293. delegate.URLSession(session, dataTask: dataTask, willCacheResponse: proposedResponse, completionHandler: completionHandler)
  294. } else {
  295. completionHandler(proposedResponse)
  296. }
  297. }
  298. // MARK: - NSURLSessionDownloadDelegate
  299. // MARK: Override Closures
  300. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didFinishDownloadingToURL:`.
  301. public var downloadTaskDidFinishDownloadingToURL: ((NSURLSession, NSURLSessionDownloadTask, NSURL) -> Void)?
  302. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`.
  303. public var downloadTaskDidWriteData: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
  304. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`.
  305. public var downloadTaskDidResumeAtOffset: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64) -> Void)?
  306. // MARK: Delegate Methods
  307. public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
  308. if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
  309. downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
  310. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  311. delegate.URLSession(session, downloadTask: downloadTask, didFinishDownloadingToURL: location)
  312. }
  313. }
  314. public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
  315. if let downloadTaskDidWriteData = downloadTaskDidWriteData {
  316. downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
  317. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  318. delegate.URLSession(session, downloadTask: downloadTask, didWriteData: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)
  319. }
  320. }
  321. public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
  322. if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
  323. downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
  324. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  325. delegate.URLSession(session, downloadTask: downloadTask, didResumeAtOffset: fileOffset, expectedTotalBytes: expectedTotalBytes)
  326. }
  327. }
  328. }
  329. }