Alamofire.swift 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689
  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. /// Alamofire errors
  24. public let AlamofireErrorDomain = "com.alamofire.error"
  25. /**
  26. HTTP method definitions.
  27. See http://tools.ietf.org/html/rfc7231#section-4.3
  28. */
  29. public enum Method: String {
  30. case OPTIONS = "OPTIONS"
  31. case GET = "GET"
  32. case HEAD = "HEAD"
  33. case POST = "POST"
  34. case PUT = "PUT"
  35. case PATCH = "PATCH"
  36. case DELETE = "DELETE"
  37. case TRACE = "TRACE"
  38. case CONNECT = "CONNECT"
  39. }
  40. /**
  41. Used to specify the way in which a set of parameters are applied to a URL request.
  42. */
  43. public enum ParameterEncoding {
  44. /**
  45. A query string to be set as or appended to any existing URL query for `GET`, `HEAD`, and `DELETE` requests, or set as the body for requests with any other HTTP method. The `Content-Type` HTTP header field of an encoded request with HTTP body is set to `application/x-www-form-urlencoded`. Since there is no published specification for how to encode collection types, the convention of appending `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested dictionary values (`foo[bar]=baz`).
  46. */
  47. case URL
  48. /**
  49. Uses `NSJSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`.
  50. */
  51. case JSON
  52. /**
  53. Uses `NSPropertyListSerialization` to create a plist representation of the parameters object, according to the associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/x-plist`.
  54. */
  55. case PropertyList(NSPropertyListFormat, NSPropertyListWriteOptions)
  56. /**
  57. Uses the associated closure value to construct a new request given an existing request and parameters.
  58. */
  59. case Custom((URLRequestConvertible, [String: AnyObject]?) -> (NSURLRequest, NSError?))
  60. /**
  61. Creates a URL request by encoding parameters and applying them onto an existing request.
  62. :param: URLRequest The request to have parameters applied
  63. :param: parameters The parameters to apply
  64. :returns: A tuple containing the constructed request and the error that occurred during parameter encoding, if any.
  65. */
  66. public func encode(URLRequest: URLRequestConvertible, parameters: [String: AnyObject]?) -> (NSURLRequest, NSError?) {
  67. if parameters == nil {
  68. return (URLRequest.URLRequest, nil)
  69. }
  70. var mutableURLRequest: NSMutableURLRequest! = URLRequest.URLRequest.mutableCopy() as! NSMutableURLRequest
  71. var error: NSError? = nil
  72. switch self {
  73. case .URL:
  74. func query(parameters: [String: AnyObject]) -> String {
  75. var components: [(String, String)] = []
  76. for key in sorted(Array(parameters.keys), <) {
  77. let value: AnyObject! = parameters[key]
  78. components += self.queryComponents(key, value)
  79. }
  80. return join("&", components.map{"\($0)=\($1)"} as [String])
  81. }
  82. func encodesParametersInURL(method: Method) -> Bool {
  83. switch method {
  84. case .GET, .HEAD, .DELETE:
  85. return true
  86. default:
  87. return false
  88. }
  89. }
  90. let method = Method(rawValue: mutableURLRequest.HTTPMethod)
  91. if method != nil && encodesParametersInURL(method!) {
  92. if let URLComponents = NSURLComponents(URL: mutableURLRequest.URL!, resolvingAgainstBaseURL: false) {
  93. URLComponents.percentEncodedQuery = (URLComponents.percentEncodedQuery != nil ? URLComponents.percentEncodedQuery! + "&" : "") + query(parameters!)
  94. mutableURLRequest.URL = URLComponents.URL
  95. }
  96. } else {
  97. if mutableURLRequest.valueForHTTPHeaderField("Content-Type") == nil {
  98. mutableURLRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
  99. }
  100. mutableURLRequest.HTTPBody = query(parameters!).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
  101. }
  102. case .JSON:
  103. let options = NSJSONWritingOptions.allZeros
  104. if let data = NSJSONSerialization.dataWithJSONObject(parameters!, options: options, error: &error) {
  105. mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
  106. mutableURLRequest.HTTPBody = data
  107. }
  108. case .PropertyList(let (format, options)):
  109. if let data = NSPropertyListSerialization.dataWithPropertyList(parameters!, format: format, options: options, error: &error) {
  110. mutableURLRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type")
  111. mutableURLRequest.HTTPBody = data
  112. }
  113. case .Custom(let closure):
  114. return closure(mutableURLRequest, parameters)
  115. }
  116. return (mutableURLRequest, error)
  117. }
  118. func queryComponents(key: String, _ value: AnyObject) -> [(String, String)] {
  119. var components: [(String, String)] = []
  120. if let dictionary = value as? [String: AnyObject] {
  121. for (nestedKey, value) in dictionary {
  122. components += queryComponents("\(key)[\(nestedKey)]", value)
  123. }
  124. } else if let array = value as? [AnyObject] {
  125. for value in array {
  126. components += queryComponents("\(key)[]", value)
  127. }
  128. } else {
  129. components.extend([(escape(key), escape("\(value)"))])
  130. }
  131. return components
  132. }
  133. func escape(string: String) -> String {
  134. let legalURLCharactersToBeEscaped: CFStringRef = ":&=;+!@#$()',*"
  135. return CFURLCreateStringByAddingPercentEscapes(nil, string, nil, legalURLCharactersToBeEscaped, CFStringBuiltInEncodings.UTF8.rawValue) as String
  136. }
  137. }
  138. // MARK: - URLStringConvertible
  139. /**
  140. Types adopting the `URLStringConvertible` protocol can be used to construct URL strings, which are then used to construct URL requests.
  141. */
  142. public protocol URLStringConvertible {
  143. /**
  144. A URL that conforms to RFC 2396.
  145. Methods accepting a `URLStringConvertible` type parameter parse it according to RFCs 1738 and 1808.
  146. See http://tools.ietf.org/html/rfc2396
  147. See http://tools.ietf.org/html/rfc1738
  148. See http://tools.ietf.org/html/rfc1808
  149. */
  150. var URLString: String { get }
  151. }
  152. extension String: URLStringConvertible {
  153. public var URLString: String {
  154. return self
  155. }
  156. }
  157. extension NSURL: URLStringConvertible {
  158. public var URLString: String {
  159. return absoluteString!
  160. }
  161. }
  162. extension NSURLComponents: URLStringConvertible {
  163. public var URLString: String {
  164. return URL!.URLString
  165. }
  166. }
  167. extension NSURLRequest: URLStringConvertible {
  168. public var URLString: String {
  169. return URL!.URLString
  170. }
  171. }
  172. // MARK: - URLRequestConvertible
  173. /**
  174. Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests.
  175. */
  176. public protocol URLRequestConvertible {
  177. /// The URL request.
  178. var URLRequest: NSURLRequest { get }
  179. }
  180. extension NSURLRequest: URLRequestConvertible {
  181. public var URLRequest: NSURLRequest {
  182. return self
  183. }
  184. }
  185. // MARK: -
  186. /**
  187. Responsible for creating and managing `Request` objects, as well as their underlying `NSURLSession`.
  188. When finished with a manager, be sure to call either `session.finishTasksAndInvalidate()` or `session.invalidateAndCancel()` before deinitialization.
  189. */
  190. public class Manager {
  191. /**
  192. A shared instance of `Manager`, used by top-level Alamofire request methods, and suitable for use directly for any ad hoc requests.
  193. */
  194. public static let sharedInstance: Manager = {
  195. let configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
  196. configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
  197. return Manager(configuration: configuration)
  198. }()
  199. /**
  200. Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers.
  201. :returns: The default header values.
  202. */
  203. public static let defaultHTTPHeaders: [String: String] = {
  204. // Accept-Encoding HTTP Header; see http://tools.ietf.org/html/rfc7230#section-4.2.3
  205. let acceptEncoding: String = "gzip;q=1.0,compress;q=0.5"
  206. // Accept-Language HTTP Header; see http://tools.ietf.org/html/rfc7231#section-5.3.5
  207. let acceptLanguage: String = {
  208. var components: [String] = []
  209. for (index, languageCode) in enumerate(NSLocale.preferredLanguages() as! [String]) {
  210. let q = 1.0 - (Double(index) * 0.1)
  211. components.append("\(languageCode);q=\(q)")
  212. if q <= 0.5 {
  213. break
  214. }
  215. }
  216. return join(",", components)
  217. }()
  218. // User-Agent Header; see http://tools.ietf.org/html/rfc7231#section-5.5.3
  219. let userAgent: String = {
  220. if let info = NSBundle.mainBundle().infoDictionary {
  221. let executable: AnyObject = info[kCFBundleExecutableKey] ?? "Unknown"
  222. let bundle: AnyObject = info[kCFBundleIdentifierKey] ?? "Unknown"
  223. let version: AnyObject = info[kCFBundleVersionKey] ?? "Unknown"
  224. let os: AnyObject = NSProcessInfo.processInfo().operatingSystemVersionString ?? "Unknown"
  225. var mutableUserAgent = NSMutableString(string: "\(executable)/\(bundle) (\(version); OS \(os))") as CFMutableString
  226. let transform = NSString(string: "Any-Latin; Latin-ASCII; [:^ASCII:] Remove") as CFString
  227. if CFStringTransform(mutableUserAgent, nil, transform, 0) == 1 {
  228. return mutableUserAgent as NSString as! String
  229. }
  230. }
  231. return "Alamofire"
  232. }()
  233. return ["Accept-Encoding": acceptEncoding,
  234. "Accept-Language": acceptLanguage,
  235. "User-Agent": userAgent]
  236. }()
  237. private let queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
  238. /// The underlying session.
  239. public let session: NSURLSession
  240. /// The session delegate handling all the task and session delegate callbacks.
  241. public let delegate: SessionDelegate
  242. /// Whether to start requests immediately after being constructed. `true` by default.
  243. public var startRequestsImmediately: Bool = true
  244. /// 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.
  245. public var backgroundCompletionHandler: (() -> Void)?
  246. /**
  247. :param: configuration The configuration used to construct the managed session.
  248. */
  249. required public init(configuration: NSURLSessionConfiguration? = nil) {
  250. self.delegate = SessionDelegate()
  251. self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
  252. self.delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
  253. if let strongSelf = self {
  254. strongSelf.backgroundCompletionHandler?()
  255. }
  256. }
  257. }
  258. // MARK: -
  259. /**
  260. Creates a request for the specified method, URL string, parameters, and parameter encoding.
  261. :param: method The HTTP method.
  262. :param: URLString The URL string.
  263. :param: parameters The parameters. `nil` by default.
  264. :param: encoding The parameter encoding. `.URL` by default.
  265. :returns: The created request.
  266. */
  267. public func request(method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {
  268. return request(encoding.encode(URLRequest(method, URLString), parameters: parameters).0)
  269. }
  270. /**
  271. Creates a request for the specified URL request.
  272. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  273. :param: URLRequest The URL request
  274. :returns: The created request.
  275. */
  276. public func request(URLRequest: URLRequestConvertible) -> Request {
  277. var dataTask: NSURLSessionDataTask?
  278. dispatch_sync(queue) {
  279. dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest)
  280. }
  281. let request = Request(session: session, task: dataTask!)
  282. delegate[request.delegate.task] = request.delegate
  283. if startRequestsImmediately {
  284. request.resume()
  285. }
  286. return request
  287. }
  288. /**
  289. Responsible for handling all delegate callbacks for the underlying session.
  290. */
  291. public final class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
  292. private var subdelegates: [Int: Request.TaskDelegate] = [:]
  293. private let subdelegateQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT)
  294. private subscript(task: NSURLSessionTask) -> Request.TaskDelegate? {
  295. get {
  296. var subdelegate: Request.TaskDelegate?
  297. dispatch_sync(subdelegateQueue) {
  298. subdelegate = self.subdelegates[task.taskIdentifier]
  299. }
  300. return subdelegate
  301. }
  302. set {
  303. dispatch_barrier_async(subdelegateQueue) {
  304. self.subdelegates[task.taskIdentifier] = newValue
  305. }
  306. }
  307. }
  308. // MARK: NSURLSessionDelegate
  309. /// NSURLSessionDelegate override closure for `URLSession:didBecomeInvalidWithError:` method.
  310. public var sessionDidBecomeInvalidWithError: ((NSURLSession, NSError?) -> Void)?
  311. /// NSURLSessionDelegate override closure for `URLSession:didReceiveChallenge:completionHandler:` method.
  312. public var sessionDidReceiveChallenge: ((NSURLSession, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential!))?
  313. /// NSURLSessionDelegate override closure for `URLSessionDidFinishEventsForBackgroundURLSession:` method.
  314. public var sessionDidFinishEventsForBackgroundURLSession: ((NSURLSession) -> Void)?
  315. public func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
  316. sessionDidBecomeInvalidWithError?(session, error)
  317. }
  318. public func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void)) {
  319. if sessionDidReceiveChallenge != nil {
  320. completionHandler(sessionDidReceiveChallenge!(session, challenge))
  321. } else {
  322. completionHandler(.PerformDefaultHandling, nil)
  323. }
  324. }
  325. public func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession) {
  326. sessionDidFinishEventsForBackgroundURLSession?(session)
  327. }
  328. // MARK: NSURLSessionTaskDelegate
  329. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:`.
  330. public var taskWillPerformHTTPRedirection: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest) -> (NSURLRequest!))?
  331. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didReceiveChallenge:completionHandler:`.
  332. public var taskDidReceiveChallenge: ((NSURLSession, NSURLSessionTask, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential!))?
  333. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:session:task:needNewBodyStream:`.
  334. public var taskNeedNewBodyStream: ((NSURLSession, NSURLSessionTask) -> (NSInputStream!))?
  335. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
  336. public var taskDidSendBodyData: ((NSURLSession, NSURLSessionTask, Int64, Int64, Int64) -> Void)?
  337. /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didCompleteWithError:`.
  338. public var taskDidComplete: ((NSURLSession, NSURLSessionTask, NSError?) -> Void)?
  339. public func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: ((NSURLRequest!) -> Void)) {
  340. var redirectRequest: NSURLRequest! = request
  341. if taskWillPerformHTTPRedirection != nil {
  342. redirectRequest = taskWillPerformHTTPRedirection!(session, task, response, request)
  343. }
  344. completionHandler(redirectRequest)
  345. }
  346. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void)) {
  347. if taskDidReceiveChallenge != nil {
  348. completionHandler(taskDidReceiveChallenge!(session, task, challenge))
  349. } else if let delegate = self[task] {
  350. delegate.URLSession(session, task: task, didReceiveChallenge: challenge, completionHandler: completionHandler)
  351. } else {
  352. URLSession(session, didReceiveChallenge: challenge, completionHandler: completionHandler)
  353. }
  354. }
  355. public func URLSession(session: NSURLSession, task: NSURLSessionTask, needNewBodyStream completionHandler: ((NSInputStream!) -> Void)) {
  356. if taskNeedNewBodyStream != nil {
  357. completionHandler(taskNeedNewBodyStream!(session, task))
  358. } else if let delegate = self[task] {
  359. delegate.URLSession(session, task: task, needNewBodyStream: completionHandler)
  360. }
  361. }
  362. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
  363. if taskDidSendBodyData != nil {
  364. taskDidSendBodyData!(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
  365. } else if let delegate = self[task] as? Request.UploadTaskDelegate {
  366. delegate.URLSession(session, task: task, didSendBodyData: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend)
  367. }
  368. }
  369. public func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
  370. if taskDidComplete != nil {
  371. taskDidComplete!(session, task, error)
  372. } else if let delegate = self[task] {
  373. delegate.URLSession(session, task: task, didCompleteWithError: error)
  374. self[task] = nil
  375. }
  376. }
  377. // MARK: NSURLSessionDataDelegate
  378. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveResponse:completionHandler:`.
  379. public var dataTaskDidReceiveResponse: ((NSURLSession, NSURLSessionDataTask, NSURLResponse) -> (NSURLSessionResponseDisposition))?
  380. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didBecomeDownloadTask:`.
  381. public var dataTaskDidBecomeDownloadTask: ((NSURLSession, NSURLSessionDataTask, NSURLSessionDownloadTask) -> Void)?
  382. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveData:`.
  383. public var dataTaskDidReceiveData: ((NSURLSession, NSURLSessionDataTask, NSData) -> Void)?
  384. /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:willCacheResponse:completionHandler:`.
  385. public var dataTaskWillCacheResponse: ((NSURLSession, NSURLSessionDataTask, NSCachedURLResponse) -> (NSCachedURLResponse!))?
  386. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: ((NSURLSessionResponseDisposition) -> Void)) {
  387. var disposition: NSURLSessionResponseDisposition = .Allow
  388. if dataTaskDidReceiveResponse != nil {
  389. disposition = dataTaskDidReceiveResponse!(session, dataTask, response)
  390. }
  391. completionHandler(disposition)
  392. }
  393. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask) {
  394. if dataTaskDidBecomeDownloadTask != nil {
  395. dataTaskDidBecomeDownloadTask!(session, dataTask, downloadTask)
  396. } else {
  397. let downloadDelegate = Request.DownloadTaskDelegate(task: downloadTask)
  398. self[downloadTask] = downloadDelegate
  399. }
  400. }
  401. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
  402. if dataTaskDidReceiveData != nil {
  403. dataTaskDidReceiveData!(session, dataTask, data)
  404. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  405. delegate.URLSession(session, dataTask: dataTask, didReceiveData: data)
  406. }
  407. }
  408. public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: ((NSCachedURLResponse!) -> Void)) {
  409. if dataTaskWillCacheResponse != nil {
  410. completionHandler(dataTaskWillCacheResponse!(session, dataTask, proposedResponse))
  411. } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
  412. delegate.URLSession(session, dataTask: dataTask, willCacheResponse: proposedResponse, completionHandler: completionHandler)
  413. } else {
  414. completionHandler(proposedResponse)
  415. }
  416. }
  417. // MARK: NSURLSessionDownloadDelegate
  418. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didFinishDownloadingToURL:`.
  419. public var downloadTaskDidFinishDownloadingToURL: ((NSURLSession, NSURLSessionDownloadTask, NSURL) -> Void)?
  420. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`.
  421. public var downloadTaskDidWriteData: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
  422. /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`.
  423. public var downloadTaskDidResumeAtOffset: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64) -> Void)?
  424. public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
  425. if downloadTaskDidFinishDownloadingToURL != nil {
  426. downloadTaskDidFinishDownloadingToURL!(session, downloadTask, location)
  427. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  428. delegate.URLSession(session, downloadTask: downloadTask, didFinishDownloadingToURL: location)
  429. }
  430. }
  431. public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
  432. if downloadTaskDidWriteData != nil {
  433. downloadTaskDidWriteData!(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
  434. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  435. delegate.URLSession(session, downloadTask: downloadTask, didWriteData: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)
  436. }
  437. }
  438. public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
  439. if downloadTaskDidResumeAtOffset != nil {
  440. downloadTaskDidResumeAtOffset!(session, downloadTask, fileOffset, expectedTotalBytes)
  441. } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
  442. delegate.URLSession(session, downloadTask: downloadTask, didResumeAtOffset: fileOffset, expectedTotalBytes: expectedTotalBytes)
  443. }
  444. }
  445. // MARK: NSObject
  446. public override func respondsToSelector(selector: Selector) -> Bool {
  447. switch selector {
  448. case "URLSession:didBecomeInvalidWithError:":
  449. return (sessionDidBecomeInvalidWithError != nil)
  450. case "URLSession:didReceiveChallenge:completionHandler:":
  451. return (sessionDidReceiveChallenge != nil)
  452. case "URLSessionDidFinishEventsForBackgroundURLSession:":
  453. return (sessionDidFinishEventsForBackgroundURLSession != nil)
  454. case "URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:":
  455. return (taskWillPerformHTTPRedirection != nil)
  456. case "URLSession:dataTask:didReceiveResponse:completionHandler:":
  457. return (dataTaskDidReceiveResponse != nil)
  458. case "URLSession:dataTask:willCacheResponse:completionHandler:":
  459. return (dataTaskWillCacheResponse != nil)
  460. default:
  461. return self.dynamicType.instancesRespondToSelector(selector)
  462. }
  463. }
  464. }
  465. }
  466. // MARK: -
  467. /**
  468. Responsible for sending a request and receiving the response and associated data from the server, as well as managing its underlying `NSURLSessionTask`.
  469. */
  470. public class Request {
  471. private let delegate: TaskDelegate
  472. /// The underlying task.
  473. public var task: NSURLSessionTask { return delegate.task }
  474. /// The session belonging to the underlying task.
  475. public let session: NSURLSession
  476. /// The request sent or to be sent to the server.
  477. public var request: NSURLRequest { return task.originalRequest }
  478. /// The response received from the server, if any.
  479. public var response: NSHTTPURLResponse? { return task.response as? NSHTTPURLResponse }
  480. /// The progress of the request lifecycle.
  481. public var progress: NSProgress { return delegate.progress }
  482. private init(session: NSURLSession, task: NSURLSessionTask) {
  483. self.session = session
  484. switch task {
  485. case is NSURLSessionUploadTask:
  486. self.delegate = UploadTaskDelegate(task: task)
  487. case is NSURLSessionDataTask:
  488. self.delegate = DataTaskDelegate(task: task)
  489. case is NSURLSessionDownloadTask:
  490. self.delegate = DownloadTaskDelegate(task: task)
  491. default:
  492. self.delegate = TaskDelegate(task: task)
  493. }
  494. }
  495. // MARK: Authentication
  496. /**
  497. Associates an HTTP Basic credential with the request.
  498. :param: user The user.
  499. :param: password The password.
  500. :returns: The request.
  501. */
  502. public func authenticate(#user: String, password: String) -> Self {
  503. let credential = NSURLCredential(user: user, password: password, persistence: .ForSession)
  504. return authenticate(usingCredential: credential)
  505. }
  506. /**
  507. Associates a specified credential with the request.
  508. :param: credential The credential.
  509. :returns: The request.
  510. */
  511. public func authenticate(usingCredential credential: NSURLCredential) -> Self {
  512. delegate.credential = credential
  513. return self
  514. }
  515. // MARK: Progress
  516. /**
  517. Sets a closure to be called periodically during the lifecycle of the request as data is written to or read from the server.
  518. - For uploads, the progress closure returns the bytes written, total bytes written, and total bytes expected to write.
  519. - For downloads, the progress closure returns the bytes read, total bytes read, and total bytes expected to write.
  520. :param: closure The code to be executed periodically during the lifecycle of the request.
  521. :returns: The request.
  522. */
  523. public func progress(closure: ((Int64, Int64, Int64) -> Void)? = nil) -> Self {
  524. if let uploadDelegate = delegate as? UploadTaskDelegate {
  525. uploadDelegate.uploadProgress = closure
  526. } else if let dataDelegate = delegate as? DataTaskDelegate {
  527. dataDelegate.dataProgress = closure
  528. } else if let downloadDelegate = delegate as? DownloadTaskDelegate {
  529. downloadDelegate.downloadProgress = closure
  530. }
  531. return self
  532. }
  533. // MARK: Response
  534. /**
  535. A closure used by response handlers that takes a request, response, and data and returns a serialized object and any error that occured in the process.
  536. */
  537. public typealias Serializer = (NSURLRequest, NSHTTPURLResponse?, NSData?) -> (AnyObject?, NSError?)
  538. /**
  539. Creates a response serializer that returns the associated data as-is.
  540. :returns: A data response serializer.
  541. */
  542. public class func responseDataSerializer() -> Serializer {
  543. return { (request, response, data) in
  544. return (data, nil)
  545. }
  546. }
  547. /**
  548. Adds a handler to be called once the request has finished.
  549. :param: completionHandler The code to be executed once the request has finished.
  550. :returns: The request.
  551. */
  552. public func response(completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
  553. return response(serializer: Request.responseDataSerializer(), completionHandler: completionHandler)
  554. }
  555. /**
  556. Adds a handler to be called once the request has finished.
  557. :param: queue The queue on which the completion handler is dispatched.
  558. :param: serializer The closure responsible for serializing the request, response, and data.
  559. :param: completionHandler The code to be executed once the request has finished.
  560. :returns: The request.
  561. */
  562. public func response(queue: dispatch_queue_t? = nil, serializer: Serializer, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
  563. dispatch_async(delegate.queue) {
  564. let (responseObject: AnyObject?, serializationError: NSError?) = serializer(self.request, self.response, self.delegate.data)
  565. dispatch_async(queue ?? dispatch_get_main_queue()) {
  566. completionHandler(self.request, self.response, responseObject, self.delegate.error ?? serializationError)
  567. }
  568. }
  569. return self
  570. }
  571. /**
  572. Suspends the request.
  573. */
  574. public func suspend() {
  575. task.suspend()
  576. }
  577. /**
  578. Resumes the request.
  579. */
  580. public func resume() {
  581. task.resume()
  582. }
  583. /**
  584. Cancels the request.
  585. */
  586. public func cancel() {
  587. if let downloadDelegate = delegate as? DownloadTaskDelegate {
  588. downloadDelegate.downloadTask.cancelByProducingResumeData { (data) in
  589. downloadDelegate.resumeData = data
  590. }
  591. } else {
  592. task.cancel()
  593. }
  594. }
  595. class TaskDelegate: NSObject, NSURLSessionTaskDelegate {
  596. let task: NSURLSessionTask
  597. let queue: dispatch_queue_t
  598. let progress: NSProgress
  599. var data: NSData? { return nil }
  600. private(set) var error: NSError?
  601. var credential: NSURLCredential?
  602. var taskWillPerformHTTPRedirection: ((NSURLSession!, NSURLSessionTask!, NSHTTPURLResponse!, NSURLRequest!) -> (NSURLRequest!))?
  603. var taskDidReceiveChallenge: ((NSURLSession!, NSURLSessionTask!, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
  604. var taskDidSendBodyData: ((NSURLSession!, NSURLSessionTask!, Int64, Int64, Int64) -> Void)?
  605. var taskNeedNewBodyStream: ((NSURLSession!, NSURLSessionTask!) -> (NSInputStream!))?
  606. init(task: NSURLSessionTask) {
  607. self.task = task
  608. self.progress = NSProgress(totalUnitCount: 0)
  609. self.queue = {
  610. let label: String = "com.alamofire.task-\(task.taskIdentifier)"
  611. let queue = dispatch_queue_create((label as NSString).UTF8String, DISPATCH_QUEUE_SERIAL)
  612. dispatch_suspend(queue)
  613. return queue
  614. }()
  615. }
  616. // MARK: NSURLSessionTaskDelegate
  617. func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: ((NSURLRequest!) -> Void)) {
  618. var redirectRequest = request
  619. if taskWillPerformHTTPRedirection != nil {
  620. redirectRequest = taskWillPerformHTTPRedirection!(session, task, response, request)
  621. }
  622. completionHandler(redirectRequest)
  623. }
  624. func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void)) {
  625. var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling
  626. var credential: NSURLCredential?
  627. if taskDidReceiveChallenge != nil {
  628. (disposition, credential) = taskDidReceiveChallenge!(session, task, challenge)
  629. } else {
  630. if challenge.previousFailureCount > 0 {
  631. disposition = .CancelAuthenticationChallenge
  632. } else {
  633. credential = self.credential ?? session.configuration.URLCredentialStorage?.defaultCredentialForProtectionSpace(challenge.protectionSpace)
  634. if credential != nil {
  635. disposition = .UseCredential
  636. }
  637. }
  638. }
  639. completionHandler(disposition, credential)
  640. }
  641. func URLSession(session: NSURLSession, task: NSURLSessionTask, needNewBodyStream completionHandler: ((NSInputStream!) -> Void)) {
  642. var bodyStream: NSInputStream?
  643. if taskNeedNewBodyStream != nil {
  644. bodyStream = taskNeedNewBodyStream!(session, task)
  645. }
  646. completionHandler(bodyStream)
  647. }
  648. func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
  649. if error != nil {
  650. self.error = error
  651. }
  652. dispatch_resume(queue)
  653. }
  654. }
  655. class DataTaskDelegate: TaskDelegate, NSURLSessionDataDelegate {
  656. var dataTask: NSURLSessionDataTask! { return task as! NSURLSessionDataTask }
  657. private var mutableData: NSMutableData
  658. override var data: NSData? {
  659. return mutableData
  660. }
  661. private var expectedContentLength: Int64?
  662. var dataTaskDidReceiveResponse: ((NSURLSession!, NSURLSessionDataTask!, NSURLResponse!) -> (NSURLSessionResponseDisposition))?
  663. var dataTaskDidBecomeDownloadTask: ((NSURLSession!, NSURLSessionDataTask!) -> Void)?
  664. var dataTaskDidReceiveData: ((NSURLSession!, NSURLSessionDataTask!, NSData!) -> Void)?
  665. var dataTaskWillCacheResponse: ((NSURLSession!, NSURLSessionDataTask!, NSCachedURLResponse!) -> (NSCachedURLResponse))?
  666. var dataProgress: ((bytesReceived: Int64, totalBytesReceived: Int64, totalBytesExpectedToReceive: Int64) -> Void)?
  667. override init(task: NSURLSessionTask) {
  668. self.mutableData = NSMutableData()
  669. super.init(task: task)
  670. }
  671. // MARK: NSURLSessionDataDelegate
  672. func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: ((NSURLSessionResponseDisposition) -> Void)) {
  673. var disposition: NSURLSessionResponseDisposition = .Allow
  674. expectedContentLength = response.expectedContentLength
  675. if dataTaskDidReceiveResponse != nil {
  676. disposition = dataTaskDidReceiveResponse!(session, dataTask, response)
  677. }
  678. completionHandler(disposition)
  679. }
  680. func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask) {
  681. dataTaskDidBecomeDownloadTask?(session, dataTask)
  682. }
  683. func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
  684. dataTaskDidReceiveData?(session, dataTask, data)
  685. mutableData.appendData(data)
  686. if let expectedContentLength = dataTask.response?.expectedContentLength {
  687. dataProgress?(bytesReceived: Int64(data.length), totalBytesReceived: Int64(mutableData.length), totalBytesExpectedToReceive: expectedContentLength)
  688. }
  689. }
  690. func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: ((NSCachedURLResponse!) -> Void)) {
  691. var cachedResponse = proposedResponse
  692. if dataTaskWillCacheResponse != nil {
  693. cachedResponse = dataTaskWillCacheResponse!(session, dataTask, proposedResponse)
  694. }
  695. completionHandler(cachedResponse)
  696. }
  697. }
  698. }
  699. // MARK: - Validation
  700. extension Request {
  701. /**
  702. A closure used to validate a request that takes a URL request and URL response, and returns whether the request was valid.
  703. */
  704. public typealias Validation = (NSURLRequest, NSHTTPURLResponse) -> (Bool)
  705. /**
  706. Validates the request, using the specified closure.
  707. If validation fails, subsequent calls to response handlers will have an associated error.
  708. :param: validation A closure to validate the request.
  709. :returns: The request.
  710. */
  711. public func validate(validation: Validation) -> Self {
  712. dispatch_async(delegate.queue) {
  713. if self.response != nil && self.delegate.error == nil {
  714. if !validation(self.request, self.response!) {
  715. self.delegate.error = NSError(domain: AlamofireErrorDomain, code: -1, userInfo: nil)
  716. }
  717. }
  718. }
  719. return self
  720. }
  721. // MARK: Status Code
  722. /**
  723. Validates that the response has a status code in the specified range.
  724. If validation fails, subsequent calls to response handlers will have an associated error.
  725. :param: range The range of acceptable status codes.
  726. :returns: The request.
  727. */
  728. public func validate<S : SequenceType where S.Generator.Element == Int>(statusCode acceptableStatusCode: S) -> Self {
  729. return validate { (_, response) in
  730. return contains(acceptableStatusCode, response.statusCode)
  731. }
  732. }
  733. // MARK: Content-Type
  734. private struct MIMEType {
  735. let type: String
  736. let subtype: String
  737. init?(_ string: String) {
  738. let components = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).substringToIndex(string.rangeOfString(";")?.endIndex ?? string.endIndex).componentsSeparatedByString("/")
  739. if let type = components.first,
  740. subtype = components.last
  741. {
  742. self.type = type
  743. self.subtype = subtype
  744. } else {
  745. return nil
  746. }
  747. }
  748. func matches(MIME: MIMEType) -> Bool {
  749. switch (type, subtype) {
  750. case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"):
  751. return true
  752. default:
  753. return false
  754. }
  755. }
  756. }
  757. /**
  758. Validates that the response has a content type in the specified array.
  759. If validation fails, subsequent calls to response handlers will have an associated error.
  760. :param: contentType The acceptable content types, which may specify wildcard types and/or subtypes.
  761. :returns: The request.
  762. */
  763. public func validate<S : SequenceType where S.Generator.Element == String>(contentType acceptableContentTypes: S) -> Self {
  764. return validate {(_, response) in
  765. if let responseContentType = response.MIMEType,
  766. responseMIMEType = MIMEType(responseContentType)
  767. {
  768. for contentType in acceptableContentTypes {
  769. if let acceptableMIMEType = MIMEType(contentType)
  770. where acceptableMIMEType.matches(responseMIMEType)
  771. {
  772. return true
  773. }
  774. }
  775. }
  776. return false
  777. }
  778. }
  779. // MARK: Automatic
  780. /**
  781. Validates that the response has a status code in the default acceptable range of 200...299, and that the content type matches any specified in the Accept HTTP header field.
  782. If validation fails, subsequent calls to response handlers will have an associated error.
  783. :returns: The request.
  784. */
  785. public func validate() -> Self {
  786. let acceptableStatusCodes: Range<Int> = 200..<300
  787. let acceptableContentTypes: [String] = {
  788. if let accept = self.request.valueForHTTPHeaderField("Accept") {
  789. return accept.componentsSeparatedByString(",")
  790. }
  791. return ["*/*"]
  792. }()
  793. return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes)
  794. }
  795. }
  796. // MARK: - Upload
  797. extension Manager {
  798. private enum Uploadable {
  799. case Data(NSURLRequest, NSData)
  800. case File(NSURLRequest, NSURL)
  801. case Stream(NSURLRequest, NSInputStream)
  802. }
  803. private func upload(uploadable: Uploadable) -> Request {
  804. var uploadTask: NSURLSessionUploadTask!
  805. var HTTPBodyStream: NSInputStream?
  806. switch uploadable {
  807. case .Data(let request, let data):
  808. dispatch_sync(queue) {
  809. uploadTask = self.session.uploadTaskWithRequest(request, fromData: data)
  810. }
  811. case .File(let request, let fileURL):
  812. dispatch_sync(queue) {
  813. uploadTask = self.session.uploadTaskWithRequest(request, fromFile: fileURL)
  814. }
  815. case .Stream(let request, var stream):
  816. dispatch_sync(queue) {
  817. uploadTask = self.session.uploadTaskWithStreamedRequest(request)
  818. }
  819. HTTPBodyStream = stream
  820. }
  821. let request = Request(session: session, task: uploadTask)
  822. if HTTPBodyStream != nil {
  823. request.delegate.taskNeedNewBodyStream = { _, _ in
  824. return HTTPBodyStream
  825. }
  826. }
  827. delegate[request.delegate.task] = request.delegate
  828. if startRequestsImmediately {
  829. request.resume()
  830. }
  831. return request
  832. }
  833. // MARK: File
  834. /**
  835. Creates a request for uploading a file to the specified URL request.
  836. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  837. :param: URLRequest The URL request
  838. :param: file The file to upload
  839. :returns: The created upload request.
  840. */
  841. public func upload(URLRequest: URLRequestConvertible, file: NSURL) -> Request {
  842. return upload(.File(URLRequest.URLRequest, file))
  843. }
  844. /**
  845. Creates a request for uploading a file to the specified URL request.
  846. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  847. :param: method The HTTP method.
  848. :param: URLString The URL string.
  849. :param: file The file to upload
  850. :returns: The created upload request.
  851. */
  852. public func upload(method: Method, _ URLString: URLStringConvertible, file: NSURL) -> Request {
  853. return upload(URLRequest(method, URLString), file: file)
  854. }
  855. // MARK: Data
  856. /**
  857. Creates a request for uploading data to the specified URL request.
  858. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  859. :param: URLRequest The URL request
  860. :param: data The data to upload
  861. :returns: The created upload request.
  862. */
  863. public func upload(URLRequest: URLRequestConvertible, data: NSData) -> Request {
  864. return upload(.Data(URLRequest.URLRequest, data))
  865. }
  866. /**
  867. Creates a request for uploading data to the specified URL request.
  868. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  869. :param: method The HTTP method.
  870. :param: URLString The URL string.
  871. :param: data The data to upload
  872. :returns: The created upload request.
  873. */
  874. public func upload(method: Method, _ URLString: URLStringConvertible, data: NSData) -> Request {
  875. return upload(URLRequest(method, URLString), data: data)
  876. }
  877. // MARK: Stream
  878. /**
  879. Creates a request for uploading a stream to the specified URL request.
  880. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  881. :param: URLRequest The URL request
  882. :param: stream The stream to upload
  883. :returns: The created upload request.
  884. */
  885. public func upload(URLRequest: URLRequestConvertible, stream: NSInputStream) -> Request {
  886. return upload(.Stream(URLRequest.URLRequest, stream))
  887. }
  888. /**
  889. Creates a request for uploading a stream to the specified URL request.
  890. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  891. :param: method The HTTP method.
  892. :param: URLString The URL string.
  893. :param: stream The stream to upload.
  894. :returns: The created upload request.
  895. */
  896. public func upload(method: Method, _ URLString: URLStringConvertible, stream: NSInputStream) -> Request {
  897. return upload(URLRequest(method, URLString), stream: stream)
  898. }
  899. }
  900. extension Request {
  901. class UploadTaskDelegate: DataTaskDelegate {
  902. var uploadTask: NSURLSessionUploadTask! { return task as! NSURLSessionUploadTask }
  903. var uploadProgress: ((Int64, Int64, Int64) -> Void)!
  904. // MARK: NSURLSessionTaskDelegate
  905. func URLSession(session: NSURLSession!, task: NSURLSessionTask!, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
  906. progress.totalUnitCount = totalBytesExpectedToSend
  907. progress.completedUnitCount = totalBytesSent
  908. uploadProgress?(bytesSent, totalBytesSent, totalBytesExpectedToSend)
  909. }
  910. }
  911. }
  912. // MARK: - Download
  913. extension Manager {
  914. private enum Downloadable {
  915. case Request(NSURLRequest)
  916. case ResumeData(NSData)
  917. }
  918. private func download(downloadable: Downloadable, destination: Request.DownloadFileDestination) -> Request {
  919. var downloadTask: NSURLSessionDownloadTask!
  920. switch downloadable {
  921. case .Request(let request):
  922. dispatch_sync(queue) {
  923. downloadTask = self.session.downloadTaskWithRequest(request)
  924. }
  925. case .ResumeData(let resumeData):
  926. dispatch_sync(queue) {
  927. downloadTask = self.session.downloadTaskWithResumeData(resumeData)
  928. }
  929. }
  930. let request = Request(session: session, task: downloadTask)
  931. if let downloadDelegate = request.delegate as? Request.DownloadTaskDelegate {
  932. downloadDelegate.downloadTaskDidFinishDownloadingToURL = { (session, downloadTask, URL) in
  933. return destination(URL, downloadTask.response as! NSHTTPURLResponse)
  934. }
  935. }
  936. delegate[request.delegate.task] = request.delegate
  937. if startRequestsImmediately {
  938. request.resume()
  939. }
  940. return request
  941. }
  942. // MARK: Request
  943. /**
  944. Creates a download request using the shared manager instance for the specified method and URL string.
  945. :param: method The HTTP method.
  946. :param: URLString The URL string.
  947. :param: destination The closure used to determine the destination of the downloaded file.
  948. :returns: The created download request.
  949. */
  950. public func download(method: Method, _ URLString: URLStringConvertible, destination: Request.DownloadFileDestination) -> Request {
  951. return download(URLRequest(method, URLString), destination: destination)
  952. }
  953. /**
  954. Creates a request for downloading from the specified URL request.
  955. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  956. :param: URLRequest The URL request
  957. :param: destination The closure used to determine the destination of the downloaded file.
  958. :returns: The created download request.
  959. */
  960. public func download(URLRequest: URLRequestConvertible, destination: Request.DownloadFileDestination) -> Request {
  961. return download(.Request(URLRequest.URLRequest), destination: destination)
  962. }
  963. // MARK: Resume Data
  964. /**
  965. Creates a request for downloading from the resume data produced from a previous request cancellation.
  966. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  967. :param: resumeData The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional information.
  968. :param: destination The closure used to determine the destination of the downloaded file.
  969. :returns: The created download request.
  970. */
  971. public func download(resumeData: NSData, destination: Request.DownloadFileDestination) -> Request {
  972. return download(.ResumeData(resumeData), destination: destination)
  973. }
  974. }
  975. extension Request {
  976. /**
  977. A closure executed once a request has successfully completed in order to determine where to move the temporary file written to during the download process. The closure takes two arguments: the temporary file URL and the URL response, and returns a single argument: the file URL where the temporary file should be moved.
  978. */
  979. public typealias DownloadFileDestination = (NSURL, NSHTTPURLResponse) -> (NSURL)
  980. /**
  981. Creates a download file destination closure which uses the default file manager to move the temporary file to a file URL in the first available directory with the specified search path directory and search path domain mask.
  982. :param: directory The search path directory. `.DocumentDirectory` by default.
  983. :param: domain The search path domain mask. `.UserDomainMask` by default.
  984. :returns: A download file destination closure.
  985. */
  986. public class func suggestedDownloadDestination(directory: NSSearchPathDirectory = .DocumentDirectory, domain: NSSearchPathDomainMask = .UserDomainMask) -> DownloadFileDestination {
  987. return { (temporaryURL, response) -> (NSURL) in
  988. if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(directory, inDomains: domain)[0] as? NSURL {
  989. return directoryURL.URLByAppendingPathComponent(response.suggestedFilename!)
  990. }
  991. return temporaryURL
  992. }
  993. }
  994. class DownloadTaskDelegate: TaskDelegate, NSURLSessionDownloadDelegate {
  995. var downloadTask: NSURLSessionDownloadTask! { return task as! NSURLSessionDownloadTask }
  996. var downloadProgress: ((Int64, Int64, Int64) -> Void)?
  997. var resumeData: NSData?
  998. override var data: NSData? { return resumeData }
  999. var downloadTaskDidFinishDownloadingToURL: ((NSURLSession!, NSURLSessionDownloadTask!, NSURL) -> (NSURL))?
  1000. var downloadTaskDidWriteData: ((NSURLSession!, NSURLSessionDownloadTask!, Int64, Int64, Int64) -> Void)?
  1001. var downloadTaskDidResumeAtOffset: ((NSURLSession!, NSURLSessionDownloadTask!, Int64, Int64) -> Void)?
  1002. // MARK: NSURLSessionDownloadDelegate
  1003. func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
  1004. if downloadTaskDidFinishDownloadingToURL != nil {
  1005. let destination = downloadTaskDidFinishDownloadingToURL!(session, downloadTask, location)
  1006. var fileManagerError: NSError?
  1007. NSFileManager.defaultManager().moveItemAtURL(location, toURL: destination, error: &fileManagerError)
  1008. if fileManagerError != nil {
  1009. error = fileManagerError
  1010. }
  1011. }
  1012. }
  1013. func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
  1014. progress.totalUnitCount = totalBytesExpectedToWrite
  1015. progress.completedUnitCount = totalBytesWritten
  1016. downloadTaskDidWriteData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
  1017. downloadProgress?(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
  1018. }
  1019. func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
  1020. progress.totalUnitCount = expectedTotalBytes
  1021. progress.completedUnitCount = fileOffset
  1022. downloadTaskDidResumeAtOffset?(session, downloadTask, fileOffset, expectedTotalBytes)
  1023. }
  1024. }
  1025. }
  1026. // MARK: - Printable
  1027. extension Request: Printable {
  1028. /// The textual representation used when written to an output stream, which includes the HTTP method and URL, as well as the response status code if a response has been received.
  1029. public var description: String {
  1030. var components: [String] = []
  1031. if request.HTTPMethod != nil {
  1032. components.append(request.HTTPMethod!)
  1033. }
  1034. components.append(request.URL!.absoluteString!)
  1035. if response != nil {
  1036. components.append("(\(response!.statusCode))")
  1037. }
  1038. return join(" ", components)
  1039. }
  1040. }
  1041. extension Request: DebugPrintable {
  1042. func cURLRepresentation() -> String {
  1043. var components: [String] = ["$ curl -i"]
  1044. let URL = request.URL
  1045. if request.HTTPMethod != nil && request.HTTPMethod != "GET" {
  1046. components.append("-X \(request.HTTPMethod!)")
  1047. }
  1048. if let credentialStorage = self.session.configuration.URLCredentialStorage {
  1049. let protectionSpace = NSURLProtectionSpace(host: URL!.host!, port: URL!.port?.integerValue ?? 0, `protocol`: URL!.scheme!, realm: URL!.host!, authenticationMethod: NSURLAuthenticationMethodHTTPBasic)
  1050. if let credentials = credentialStorage.credentialsForProtectionSpace(protectionSpace)?.values.array {
  1051. for credential: NSURLCredential in (credentials as! [NSURLCredential]) {
  1052. components.append("-u \(credential.user!):\(credential.password!)")
  1053. }
  1054. } else {
  1055. if let credential = delegate.credential {
  1056. components.append("-u \(credential.user!):\(credential.password!)")
  1057. }
  1058. }
  1059. }
  1060. // Temporarily disabled on OS X due to build failure for CocoaPods
  1061. // See https://github.com/CocoaPods/swift/issues/24
  1062. #if !os(OSX)
  1063. if let cookieStorage = session.configuration.HTTPCookieStorage,
  1064. cookies = cookieStorage.cookiesForURL(URL!) as? [NSHTTPCookie]
  1065. where !cookies.isEmpty
  1066. {
  1067. let string = cookies.reduce(""){ $0 + "\($1.name)=\($1.value ?? String());" }
  1068. components.append("-b \"\(string.substringToIndex(string.endIndex.predecessor()))\"")
  1069. }
  1070. #endif
  1071. if request.allHTTPHeaderFields != nil {
  1072. for (field, value) in request.allHTTPHeaderFields! {
  1073. switch field {
  1074. case "Cookie":
  1075. continue
  1076. default:
  1077. components.append("-H \"\(field): \(value)\"")
  1078. }
  1079. }
  1080. }
  1081. if session.configuration.HTTPAdditionalHeaders != nil {
  1082. for (field, value) in session.configuration.HTTPAdditionalHeaders! {
  1083. switch field {
  1084. case "Cookie":
  1085. continue
  1086. default:
  1087. components.append("-H \"\(field): \(value)\"")
  1088. }
  1089. }
  1090. }
  1091. if let HTTPBody = request.HTTPBody,
  1092. escapedBody = NSString(data: HTTPBody, encoding: NSUTF8StringEncoding)?.stringByReplacingOccurrencesOfString("\"", withString: "\\\"")
  1093. {
  1094. components.append("-d \"\(escapedBody)\"")
  1095. }
  1096. components.append("\"\(URL!.absoluteString!)\"")
  1097. return join(" \\\n\t", components)
  1098. }
  1099. /// The textual representation used when written to an output stream, in the form of a cURL command.
  1100. public var debugDescription: String {
  1101. return cURLRepresentation()
  1102. }
  1103. }
  1104. // MARK: - Response Serializers
  1105. // MARK: String
  1106. extension Request {
  1107. /**
  1108. Creates a response serializer that returns a string initialized from the response data with the specified string encoding.
  1109. :param: encoding The string encoding. If `nil`, the string encoding will be determined from the server response, falling back to the default HTTP default character set, ISO-8859-1.
  1110. :returns: A string response serializer.
  1111. */
  1112. public class func stringResponseSerializer(var encoding: NSStringEncoding? = nil) -> Serializer {
  1113. return { (_, response, data) in
  1114. if data == nil || data?.length == 0 {
  1115. return (nil, nil)
  1116. }
  1117. if encoding == nil {
  1118. if let encodingName = response?.textEncodingName {
  1119. encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName))
  1120. }
  1121. }
  1122. let string = NSString(data: data!, encoding: encoding ?? NSISOLatin1StringEncoding)
  1123. return (string, nil)
  1124. }
  1125. }
  1126. /**
  1127. Adds a handler to be called once the request has finished.
  1128. :param: encoding The string encoding. If `nil`, the string encoding will be determined from the server response, falling back to the default HTTP default character set, ISO-8859-1.
  1129. :param: completionHandler A closure to be executed once the request has finished. The closure takes 4 arguments: the URL request, the URL response, if one was received, the string, if one could be created from the URL response and data, and any error produced while creating the string.
  1130. :returns: The request.
  1131. */
  1132. public func responseString(encoding: NSStringEncoding? = nil, completionHandler: (NSURLRequest, NSHTTPURLResponse?, String?, NSError?) -> Void) -> Self {
  1133. return response(serializer: Request.stringResponseSerializer(encoding: encoding), completionHandler: { request, response, string, error in
  1134. completionHandler(request, response, string as? String, error)
  1135. })
  1136. }
  1137. }
  1138. // MARK: JSON
  1139. extension Request {
  1140. /**
  1141. Creates a response serializer that returns a JSON object constructed from the response data using `NSJSONSerialization` with the specified reading options.
  1142. :param: options The JSON serialization reading options. `.AllowFragments` by default.
  1143. :returns: A JSON object response serializer.
  1144. */
  1145. public class func JSONResponseSerializer(options: NSJSONReadingOptions = .AllowFragments) -> Serializer {
  1146. return { (request, response, data) in
  1147. if data == nil || data?.length == 0 {
  1148. return (nil, nil)
  1149. }
  1150. var serializationError: NSError?
  1151. let JSON: AnyObject? = NSJSONSerialization.JSONObjectWithData(data!, options: options, error: &serializationError)
  1152. return (JSON, serializationError)
  1153. }
  1154. }
  1155. /**
  1156. Adds a handler to be called once the request has finished.
  1157. :param: options The JSON serialization reading options. `.AllowFragments` by default.
  1158. :param: completionHandler A closure to be executed once the request has finished. The closure takes 4 arguments: the URL request, the URL response, if one was received, the JSON object, if one could be created from the URL response and data, and any error produced while creating the JSON object.
  1159. :returns: The request.
  1160. */
  1161. public func responseJSON(options: NSJSONReadingOptions = .AllowFragments, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
  1162. return response(serializer: Request.JSONResponseSerializer(options: options), completionHandler: { (request, response, JSON, error) in
  1163. completionHandler(request, response, JSON, error)
  1164. })
  1165. }
  1166. }
  1167. // MARK: Property List
  1168. extension Request {
  1169. /**
  1170. Creates a response serializer that returns an object constructed from the response data using `NSPropertyListSerialization` with the specified reading options.
  1171. :param: options The property list reading options. `0` by default.
  1172. :returns: A property list object response serializer.
  1173. */
  1174. public class func propertyListResponseSerializer(options: NSPropertyListReadOptions = 0) -> Serializer {
  1175. return { (request, response, data) in
  1176. if data == nil || data?.length == 0 {
  1177. return (nil, nil)
  1178. }
  1179. var propertyListSerializationError: NSError?
  1180. let plist: AnyObject? = NSPropertyListSerialization.propertyListWithData(data!, options: options, format: nil, error: &propertyListSerializationError)
  1181. return (plist, propertyListSerializationError)
  1182. }
  1183. }
  1184. /**
  1185. Adds a handler to be called once the request has finished.
  1186. :param: options The property list reading options. `0` by default.
  1187. :param: completionHandler A closure to be executed once the request has finished. The closure takes 4 arguments: the URL request, the URL response, if one was received, the property list, if one could be created from the URL response and data, and any error produced while creating the property list.
  1188. :returns: The request.
  1189. */
  1190. public func responsePropertyList(options: NSPropertyListReadOptions = 0, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
  1191. return response(serializer: Request.propertyListResponseSerializer(options: options), completionHandler: { (request, response, plist, error) in
  1192. completionHandler(request, response, plist, error)
  1193. })
  1194. }
  1195. }
  1196. // MARK: - Convenience -
  1197. private func URLRequest(method: Method, URL: URLStringConvertible) -> NSURLRequest {
  1198. let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: URL.URLString)!)
  1199. mutableURLRequest.HTTPMethod = method.rawValue
  1200. return mutableURLRequest
  1201. }
  1202. // MARK: - Request
  1203. /**
  1204. Creates a request using the shared manager instance for the specified method, URL string, parameters, and parameter encoding.
  1205. :param: method The HTTP method.
  1206. :param: URLString The URL string.
  1207. :param: parameters The parameters. `nil` by default.
  1208. :param: encoding The parameter encoding. `.URL` by default.
  1209. :returns: The created request.
  1210. */
  1211. public func request(method: Method, URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {
  1212. return Manager.sharedInstance.request(method, URLString, parameters: parameters, encoding: encoding)
  1213. }
  1214. /**
  1215. Creates a request using the shared manager instance for the specified URL request.
  1216. If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  1217. :param: URLRequest The URL request
  1218. :returns: The created request.
  1219. */
  1220. public func request(URLRequest: URLRequestConvertible) -> Request {
  1221. return Manager.sharedInstance.request(URLRequest.URLRequest)
  1222. }
  1223. // MARK: - Upload
  1224. // MARK: File
  1225. /**
  1226. Creates an upload request using the shared manager instance for the specified method, URL string, and file.
  1227. :param: method The HTTP method.
  1228. :param: URLString The URL string.
  1229. :param: file The file to upload.
  1230. :returns: The created upload request.
  1231. */
  1232. public func upload(method: Method, URLString: URLStringConvertible, file: NSURL) -> Request {
  1233. return Manager.sharedInstance.upload(method, URLString, file: file)
  1234. }
  1235. /**
  1236. Creates an upload request using the shared manager instance for the specified URL request and file.
  1237. :param: URLRequest The URL request.
  1238. :param: file The file to upload.
  1239. :returns: The created upload request.
  1240. */
  1241. public func upload(URLRequest: URLRequestConvertible, file: NSURL) -> Request {
  1242. return Manager.sharedInstance.upload(URLRequest, file: file)
  1243. }
  1244. // MARK: Data
  1245. /**
  1246. Creates an upload request using the shared manager instance for the specified method, URL string, and data.
  1247. :param: method The HTTP method.
  1248. :param: URLString The URL string.
  1249. :param: data The data to upload.
  1250. :returns: The created upload request.
  1251. */
  1252. public func upload(method: Method, URLString: URLStringConvertible, data: NSData) -> Request {
  1253. return Manager.sharedInstance.upload(method, URLString, data: data)
  1254. }
  1255. /**
  1256. Creates an upload request using the shared manager instance for the specified URL request and data.
  1257. :param: URLRequest The URL request.
  1258. :param: data The data to upload.
  1259. :returns: The created upload request.
  1260. */
  1261. public func upload(URLRequest: URLRequestConvertible, data: NSData) -> Request {
  1262. return Manager.sharedInstance.upload(URLRequest, data: data)
  1263. }
  1264. // MARK: Stream
  1265. /**
  1266. Creates an upload request using the shared manager instance for the specified method, URL string, and stream.
  1267. :param: method The HTTP method.
  1268. :param: URLString The URL string.
  1269. :param: stream The stream to upload.
  1270. :returns: The created upload request.
  1271. */
  1272. public func upload(method: Method, URLString: URLStringConvertible, stream: NSInputStream) -> Request {
  1273. return Manager.sharedInstance.upload(method, URLString, stream: stream)
  1274. }
  1275. /**
  1276. Creates an upload request using the shared manager instance for the specified URL request and stream.
  1277. :param: URLRequest The URL request.
  1278. :param: stream The stream to upload.
  1279. :returns: The created upload request.
  1280. */
  1281. public func upload(URLRequest: URLRequestConvertible, stream: NSInputStream) -> Request {
  1282. return Manager.sharedInstance.upload(URLRequest, stream: stream)
  1283. }
  1284. // MARK: - Download
  1285. // MARK: URL Request
  1286. /**
  1287. Creates a download request using the shared manager instance for the specified method and URL string.
  1288. :param: method The HTTP method.
  1289. :param: URLString The URL string.
  1290. :param: destination The closure used to determine the destination of the downloaded file.
  1291. :returns: The created download request.
  1292. */
  1293. public func download(method: Method, URLString: URLStringConvertible, destination: Request.DownloadFileDestination) -> Request {
  1294. return Manager.sharedInstance.download(method, URLString, destination: destination)
  1295. }
  1296. /**
  1297. Creates a download request using the shared manager instance for the specified URL request.
  1298. :param: URLRequest The URL request.
  1299. :param: destination The closure used to determine the destination of the downloaded file.
  1300. :returns: The created download request.
  1301. */
  1302. public func download(URLRequest: URLRequestConvertible, destination: Request.DownloadFileDestination) -> Request {
  1303. return Manager.sharedInstance.download(URLRequest, destination: destination)
  1304. }
  1305. // MARK: Resume Data
  1306. /**
  1307. Creates a request using the shared manager instance for downloading from the resume data produced from a previous request cancellation.
  1308. :param: resumeData The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional information.
  1309. :param: destination The closure used to determine the destination of the downloaded file.
  1310. :returns: The created download request.
  1311. */
  1312. public func download(resumeData data: NSData, destination: Request.DownloadFileDestination) -> Request {
  1313. return Manager.sharedInstance.download(data, destination: destination)
  1314. }