Manager.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // Alamofire.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. /**
  28. A shared instance of `Manager`, used by top-level Alamofire request methods, and suitable for use directly for any ad hoc requests.
  29. */
  30. public static let sharedInstance: Manager = {
  31. let configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
  32. configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
  33. return Manager(configuration: configuration)
  34. }()
  35. /**
  36. Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers.
  37. :returns: The default header values.
  38. */
  39. public static let defaultHTTPHeaders: [String: String] = {
  40. // Accept-Encoding HTTP Header; see http://tools.ietf.org/html/rfc7230#section-4.2.3
  41. let acceptEncoding: String = "gzip;q=1.0,compress;q=0.5"
  42. // Accept-Language HTTP Header; see http://tools.ietf.org/html/rfc7231#section-5.3.5
  43. let acceptLanguage: String = {
  44. var components: [String] = []
  45. for (index, languageCode) in enumerate(NSLocale.preferredLanguages() as! [String]) {
  46. let q = 1.0 - (Double(index) * 0.1)
  47. components.append("\(languageCode);q=\(q)")
  48. if q <= 0.5 {
  49. break
  50. }
  51. }
  52. return join(",", components)
  53. }()
  54. // User-Agent Header; see http://tools.ietf.org/html/rfc7231#section-5.5.3
  55. let userAgent: String = {
  56. if let info = NSBundle.mainBundle().infoDictionary {
  57. let executable: AnyObject = info[kCFBundleExecutableKey] ?? "Unknown"
  58. let bundle: AnyObject = info[kCFBundleIdentifierKey] ?? "Unknown"
  59. let version: AnyObject = info[kCFBundleVersionKey] ?? "Unknown"
  60. let os: AnyObject = NSProcessInfo.processInfo().operatingSystemVersionString ?? "Unknown"
  61. var mutableUserAgent = NSMutableString(string: "\(executable)/\(bundle) (\(version); OS \(os))") as CFMutableString
  62. let transform = NSString(string: "Any-Latin; Latin-ASCII; [:^ASCII:] Remove") as CFString
  63. if CFStringTransform(mutableUserAgent, nil, transform, 0) == 1 {
  64. return mutableUserAgent as NSString as! String
  65. }
  66. }
  67. return "Alamofire"
  68. }()
  69. return ["Accept-Encoding": acceptEncoding,
  70. "Accept-Language": acceptLanguage,
  71. "User-Agent": userAgent]
  72. }()
  73. let queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
  74. /// The underlying session.
  75. public let session: NSURLSession
  76. /// The session delegate handling all the task and session delegate callbacks.
  77. public let delegate: SessionDelegate
  78. /// Whether to start requests immediately after being constructed. `true` by default.
  79. public var startRequestsImmediately: Bool = true
  80. /// 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.
  81. public var backgroundCompletionHandler: (() -> Void)?
  82. /**
  83. :param: configuration The configuration used to construct the managed session.
  84. */
  85. required public init(configuration: NSURLSessionConfiguration? = nil) {
  86. self.delegate = SessionDelegate()
  87. self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
  88. self.delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
  89. if let strongSelf = self {
  90. strongSelf.backgroundCompletionHandler?()
  91. }
  92. }
  93. }
  94. deinit {
  95. self.session.invalidateAndCancel()
  96. }
  97. // MARK: -
  98. /**
  99. Creates a request for the specified method, URL string, parameters, and parameter encoding.
  100. :param: method The HTTP method.
  101. :param: URLString The URL string.
  102. :param: parameters The parameters. `nil` by default.
  103. :param: encoding The parameter encoding. `.URL` by default.
  104. :returns: The created request.
  105. */
  106. public func request(method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {
  107. return request(encoding.encode(URLRequest(method, URLString), parameters: parameters).0)
  108. }
  109. /**
  110. Creates a request for the specified URL request.
  111. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  112. :param: URLRequest The URL request
  113. :returns: The created request.
  114. */
  115. public func request(URLRequest: URLRequestConvertible) -> Request {
  116. var dataTask: NSURLSessionDataTask?
  117. dispatch_sync(queue) {
  118. dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest)
  119. }
  120. let request = Request(session: session, task: dataTask!)
  121. delegate[request.delegate.task] = request.delegate
  122. if startRequestsImmediately {
  123. request.resume()
  124. }
  125. return request
  126. }
  127. /**
  128. Responsible for handling all delegate callbacks for the underlying session.
  129. */
  130. public final class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
  131. private var subdelegates: [Int: Request.TaskDelegate] = [:]
  132. private let subdelegateQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT)
  133. subscript(task: NSURLSessionTask) -> Request.TaskDelegate? {
  134. get {
  135. var subdelegate: Request.TaskDelegate?
  136. dispatch_sync(subdelegateQueue) {
  137. subdelegate = self.subdelegates[task.taskIdentifier]
  138. }
  139. return subdelegate
  140. }
  141. set {
  142. dispatch_barrier_async(subdelegateQueue) {
  143. self.subdelegates[task.taskIdentifier] = newValue
  144. }
  145. }
  146. }
  147. // MARK: NSURLSessionDelegate
  148. /// NSURLSessionDelegate override closure for `URLSession:didBecomeInvalidWithError:` method.
  149. public var sessionDidBecomeInvalidWithError: ((NSURLSession, NSError?) -> Void)?
  150. /// NSURLSessionDelegate override closure for `URLSession:didReceiveChallenge:completionHandler:` method.
  151. public var sessionDidReceiveChallenge: ((NSURLSession, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential!))?
  152. /// NSURLSessionDelegate override closure for `URLSessionDidFinishEventsForBackgroundURLSession:` method.
  153. public var sessionDidFinishEventsForBackgroundURLSession: ((NSURLSession) -> Void)?
  154. public func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
  155. sessionDidBecomeInvalidWithError?(session, error)
  156. }
  157. public func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void)) {
  158. if sessionDidReceiveChallenge != nil {
  159. completionHandler(sessionDidReceiveChallenge!(session, challenge))
  160. } else {
  161. completionHandler(.PerformDefaultHandling, nil)
  162. }
  163. }
  164. public func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession) {
  165. sessionDidFinishEventsForBackgroundURLSession?(session)
  166. }
  167. // MARK: NSURLSessionTaskDelegate
  168. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:`.
  169. public var taskWillPerformHTTPRedirection: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest) -> (NSURLRequest!))?
  170. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didReceiveChallenge:completionHandler:`.
  171. public var taskDidReceiveChallenge: ((NSURLSession, NSURLSessionTask, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential!))?
  172. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:session:task:needNewBodyStream:`.
  173. public var taskNeedNewBodyStream: ((NSURLSession, NSURLSessionTask) -> (NSInputStream!))?
  174. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
  175. public var taskDidSendBodyData: ((NSURLSession, NSURLSessionTask, Int64, Int64, Int64) -> Void)?
  176. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didCompleteWithError:`.
  177. public var taskDidComplete: ((NSURLSession, NSURLSessionTask, NSError?) -> Void)?
  178. public func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: ((NSURLRequest!) -> Void)) {
  179. var redirectRequest: NSURLRequest? = request
  180. if taskWillPerformHTTPRedirection != nil {
  181. redirectRequest = taskWillPerformHTTPRedirection!(session, task, response, request)
  182. }
  183. completionHandler(redirectRequest)
  184. }
  185. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void)) {
  186. if taskDidReceiveChallenge != nil {
  187. completionHandler(taskDidReceiveChallenge!(session, task, challenge))
  188. } else if let delegate = self[task] {
  189. delegate.URLSession(session, task: task, didReceiveChallenge: challenge, completionHandler: completionHandler)
  190. } else {
  191. URLSession(session, didReceiveChallenge: challenge, completionHandler: completionHandler)
  192. }
  193. }
  194. public func URLSession(session: NSURLSession, task: NSURLSessionTask, needNewBodyStream completionHandler: ((NSInputStream!) -> Void)) {
  195. if taskNeedNewBodyStream != nil {
  196. completionHandler(taskNeedNewBodyStream!(session, task))
  197. } else if let delegate = self[task] {
  198. delegate.URLSession(session, task: task, needNewBodyStream: completionHandler)
  199. }
  200. }
  201. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
  202. if taskDidSendBodyData != nil {
  203. taskDidSendBodyData!(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
  204. } else if let delegate = self[task] as? Request.UploadTaskDelegate {
  205. delegate.URLSession(session, task: task, didSendBodyData: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend)
  206. }
  207. }
  208. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
  209. if taskDidComplete != nil {
  210. taskDidComplete!(session, task, error)
  211. } else if let delegate = self[task] {
  212. delegate.URLSession(session, task: task, didCompleteWithError: error)
  213. self[task] = nil
  214. }
  215. }
  216. // MARK: NSURLSessionDataDelegate
  217. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveResponse:completionHandler:`.
  218. public var dataTaskDidReceiveResponse: ((NSURLSession, NSURLSessionDataTask, NSURLResponse) -> (NSURLSessionResponseDisposition))?
  219. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didBecomeDownloadTask:`.
  220. public var dataTaskDidBecomeDownloadTask: ((NSURLSession, NSURLSessionDataTask, NSURLSessionDownloadTask) -> Void)?
  221. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveData:`.
  222. public var dataTaskDidReceiveData: ((NSURLSession, NSURLSessionDataTask, NSData) -> Void)?
  223. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:willCacheResponse:completionHandler:`.
  224. public var dataTaskWillCacheResponse: ((NSURLSession, NSURLSessionDataTask, NSCachedURLResponse) -> (NSCachedURLResponse!))?
  225. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: ((NSURLSessionResponseDisposition) -> Void)) {
  226. var disposition: NSURLSessionResponseDisposition = .Allow
  227. if dataTaskDidReceiveResponse != nil {
  228. disposition = dataTaskDidReceiveResponse!(session, dataTask, response)
  229. }
  230. completionHandler(disposition)
  231. }
  232. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask) {
  233. if dataTaskDidBecomeDownloadTask != nil {
  234. dataTaskDidBecomeDownloadTask!(session, dataTask, downloadTask)
  235. } else {
  236. let downloadDelegate = Request.DownloadTaskDelegate(task: downloadTask)
  237. self[downloadTask] = downloadDelegate
  238. }
  239. }
  240. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
  241. if dataTaskDidReceiveData != nil {
  242. dataTaskDidReceiveData!(session, dataTask, data)
  243. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  244. delegate.URLSession(session, dataTask: dataTask, didReceiveData: data)
  245. }
  246. }
  247. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: ((NSCachedURLResponse!) -> Void)) {
  248. if dataTaskWillCacheResponse != nil {
  249. completionHandler(dataTaskWillCacheResponse!(session, dataTask, proposedResponse))
  250. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  251. delegate.URLSession(session, dataTask: dataTask, willCacheResponse: proposedResponse, completionHandler: completionHandler)
  252. } else {
  253. completionHandler(proposedResponse)
  254. }
  255. }
  256. // MARK: NSURLSessionDownloadDelegate
  257. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didFinishDownloadingToURL:`.
  258. public var downloadTaskDidFinishDownloadingToURL: ((NSURLSession, NSURLSessionDownloadTask, NSURL) -> Void)?
  259. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`.
  260. public var downloadTaskDidWriteData: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
  261. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`.
  262. public var downloadTaskDidResumeAtOffset: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64) -> Void)?
  263. public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
  264. if downloadTaskDidFinishDownloadingToURL != nil {
  265. downloadTaskDidFinishDownloadingToURL!(session, downloadTask, location)
  266. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  267. delegate.URLSession(session, downloadTask: downloadTask, didFinishDownloadingToURL: location)
  268. }
  269. }
  270. public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
  271. if downloadTaskDidWriteData != nil {
  272. downloadTaskDidWriteData!(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
  273. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  274. delegate.URLSession(session, downloadTask: downloadTask, didWriteData: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)
  275. }
  276. }
  277. public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
  278. if downloadTaskDidResumeAtOffset != nil {
  279. downloadTaskDidResumeAtOffset!(session, downloadTask, fileOffset, expectedTotalBytes)
  280. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  281. delegate.URLSession(session, downloadTask: downloadTask, didResumeAtOffset: fileOffset, expectedTotalBytes: expectedTotalBytes)
  282. }
  283. }
  284. // MARK: NSObject
  285. public override func respondsToSelector(selector: Selector) -> Bool {
  286. switch selector {
  287. case "URLSession:didBecomeInvalidWithError:":
  288. return (sessionDidBecomeInvalidWithError != nil)
  289. case "URLSession:didReceiveChallenge:completionHandler:":
  290. return (sessionDidReceiveChallenge != nil)
  291. case "URLSessionDidFinishEventsForBackgroundURLSession:":
  292. return (sessionDidFinishEventsForBackgroundURLSession != nil)
  293. case "URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:":
  294. return (taskWillPerformHTTPRedirection != nil)
  295. case "URLSession:dataTask:didReceiveResponse:completionHandler:":
  296. return (dataTaskDidReceiveResponse != nil)
  297. case "URLSession:dataTask:willCacheResponse:completionHandler:":
  298. return (dataTaskWillCacheResponse != nil)
  299. default:
  300. return self.dynamicType.instancesRespondToSelector(selector)
  301. }
  302. }
  303. }
  304. }