Alamofire.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. //
  2. // Alamofire.swift
  3. //
  4. // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. import Foundation
  25. /// Types adopting the `URLStringConvertible` protocol can be used to construct URL strings, which are then used to
  26. /// construct URL requests.
  27. public protocol URLStringConvertible {
  28. /// A URL that conforms to RFC 2396.
  29. ///
  30. /// Methods accepting a `URLStringConvertible` type parameter parse it according to RFCs 1738 and 1808.
  31. ///
  32. /// See https://tools.ietf.org/html/rfc2396
  33. /// See https://tools.ietf.org/html/rfc1738
  34. /// See https://tools.ietf.org/html/rfc1808
  35. var urlString: String { get }
  36. }
  37. extension String: URLStringConvertible {
  38. /// The URL string.
  39. public var urlString: String { return self }
  40. }
  41. extension URL: URLStringConvertible {
  42. /// The URL string.
  43. public var urlString: String { return absoluteString }
  44. }
  45. extension URLComponents: URLStringConvertible {
  46. /// The URL string.
  47. public var urlString: String { return url!.urlString }
  48. }
  49. extension URLRequest: URLStringConvertible {
  50. /// The URL string.
  51. public var urlString: String { return url!.urlString }
  52. }
  53. // MARK: -
  54. /// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests.
  55. public protocol URLRequestConvertible {
  56. /// The URL request.
  57. var urlRequest: URLRequest { get }
  58. }
  59. extension URLRequest: URLRequestConvertible {
  60. /// The URL request.
  61. public var urlRequest: URLRequest { return self }
  62. }
  63. // MARK: -
  64. extension URLRequest {
  65. /// Creates an instance with the specified `method`, `urlString` and `headers`.
  66. ///
  67. /// - parameter method: The HTTP method.
  68. /// - parameter urlString: The URL string.
  69. /// - parameter headers: The HTTP headers. `nil` by default.
  70. ///
  71. /// - returns: The new `URLRequest` instance.
  72. public init(method: HTTPMethod, urlString: URLStringConvertible, headers: [String: String]? = nil) {
  73. self.init(url: URL(string: urlString.urlString)!)
  74. if let request = urlString as? URLRequest { self = request }
  75. httpMethod = method.rawValue
  76. if let headers = headers {
  77. for (headerField, headerValue) in headers {
  78. setValue(headerValue, forHTTPHeaderField: headerField)
  79. }
  80. }
  81. }
  82. }
  83. // MARK: - Data Request
  84. /// Creates a data `Request` using the default `SessionManager` to retrieve the contents of a URL based on the
  85. /// specified `urlString`, `method`, `parameters`, `encoding` and `headers`.
  86. ///
  87. /// - parameter urlString: The URL string.
  88. /// - parameter method: The HTTP method.
  89. /// - parameter parameters: The parameters. `nil` by default.
  90. /// - parameter encoding: The parameter encoding. `.url` by default.
  91. /// - parameter headers: The HTTP headers. `nil` by default.
  92. ///
  93. /// - returns: The created data `Request`.
  94. @discardableResult
  95. public func request(
  96. _ urlString: URLStringConvertible,
  97. withMethod method: HTTPMethod,
  98. parameters: [String: AnyObject]? = nil,
  99. encoding: ParameterEncoding = .url,
  100. headers: [String: String]? = nil)
  101. -> Request
  102. {
  103. return SessionManager.default.request(
  104. urlString,
  105. withMethod: method,
  106. parameters: parameters,
  107. encoding: encoding,
  108. headers: headers
  109. )
  110. }
  111. /// Creates a data `Request` using the default `SessionManager` to retrieve the contents of a URL based on the
  112. /// specified `urlRequest`.
  113. ///
  114. /// - parameter urlRequest: The URL request
  115. ///
  116. /// - returns: The created data `Request`.
  117. @discardableResult
  118. public func request(_ urlRequest: URLRequestConvertible) -> Request {
  119. return SessionManager.default.request(urlRequest.urlRequest)
  120. }
  121. // MARK: - Download Request
  122. // MARK: URL Request
  123. /// Creates a download `Request` using the default `SessionManager` to retrieve the contents of a URL based on the
  124. /// specified `urlString`, `method`, `parameters`, `encoding`, `headers` and save them to the `destination`.
  125. ///
  126. /// - parameter urlString: The URL string.
  127. /// - parameter destination: The closure used to determine the destination of the downloaded file.
  128. /// - parameter method: The HTTP method.
  129. /// - parameter parameters: The parameters. `nil` by default.
  130. /// - parameter encoding: The parameter encoding. `.url` by default.
  131. /// - parameter headers: The HTTP headers. `nil` by default.
  132. ///
  133. /// - returns: The created download `Request`.
  134. @discardableResult
  135. public func download(
  136. _ urlString: URLStringConvertible,
  137. to destination: Request.DownloadFileDestination,
  138. withMethod method: HTTPMethod,
  139. parameters: [String: AnyObject]? = nil,
  140. encoding: ParameterEncoding = .url,
  141. headers: [String: String]? = nil)
  142. -> Request
  143. {
  144. return SessionManager.default.download(
  145. urlString,
  146. to: destination,
  147. withMethod: method,
  148. parameters: parameters,
  149. encoding: encoding,
  150. headers: headers
  151. )
  152. }
  153. /// Creates a download `Request` using the default `SessionManager` to retrieve the contents of a URL based on the
  154. /// specified `urlRequest` and save them to the `destination`.
  155. ///
  156. /// - parameter urlRequest: The URL request.
  157. /// - parameter destination: The closure used to determine the destination of the downloaded file.
  158. ///
  159. /// - returns: The created download `Request`.
  160. @discardableResult
  161. public func download(
  162. _ urlRequest: URLRequestConvertible,
  163. to destination: Request.DownloadFileDestination)
  164. -> Request
  165. {
  166. return SessionManager.default.download(urlRequest, to: destination)
  167. }
  168. // MARK: Resume Data
  169. /// Creates a download `Request` using the default `SessionManager` from the `resumeData` produced from a
  170. /// previous request cancellation to retrieve the contents of the original request and save them to the `destination`.
  171. ///
  172. /// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask`
  173. /// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional
  174. /// information.
  175. /// - parameter destination: The closure used to determine the destination of the downloaded file.
  176. ///
  177. /// - returns: The created download `Request`.
  178. @discardableResult
  179. public func download(resourceWithin resumeData: Data, to destination: Request.DownloadFileDestination) -> Request {
  180. return SessionManager.default.download(resourceWithin: resumeData, to: destination)
  181. }
  182. // MARK: - Upload Request
  183. // MARK: File
  184. /// Creates an upload `Request` using the default `SessionManager` from the specified `method`, `urlString`
  185. /// and `headers` for uploading the `file`.
  186. ///
  187. /// - parameter file: The file to upload.
  188. /// - parameter method: The HTTP method.
  189. /// - parameter urlString: The URL string.
  190. /// - parameter headers: The HTTP headers. `nil` by default.
  191. ///
  192. /// - returns: The created upload `Request`.
  193. @discardableResult
  194. public func upload(
  195. _ fileURL: URL,
  196. to urlString: URLStringConvertible,
  197. withMethod method: HTTPMethod,
  198. headers: [String: String]? = nil)
  199. -> Request
  200. {
  201. return SessionManager.default.upload(fileURL, to: urlString, withMethod: method, headers: headers)
  202. }
  203. /// Creates a upload `Request` using the default `SessionManager` from the specified `urlRequest` for
  204. /// uploading the `file`.
  205. ///
  206. /// - parameter file: The file to upload.
  207. /// - parameter urlRequest: The URL request.
  208. ///
  209. /// - returns: The created upload `Request`.
  210. @discardableResult
  211. public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> Request {
  212. return SessionManager.default.upload(fileURL, with: urlRequest)
  213. }
  214. // MARK: Data
  215. /// Creates an upload `Request` using the default `SessionManager` from the specified `method`, `urlString`
  216. /// and `headers` for uploading the `data`.
  217. ///
  218. /// - parameter data: The data to upload.
  219. /// - parameter urlString: The URL string.
  220. /// - parameter method: The HTTP method.
  221. /// - parameter headers: The HTTP headers. `nil` by default.
  222. ///
  223. /// - returns: The created upload `Request`.
  224. @discardableResult
  225. public func upload(
  226. _ data: Data,
  227. to urlString: URLStringConvertible,
  228. withMethod method: HTTPMethod,
  229. headers: [String: String]? = nil)
  230. -> Request
  231. {
  232. return SessionManager.default.upload(data, to: urlString, withMethod: method, headers: headers)
  233. }
  234. /// Creates an upload `Request` using the default `SessionManager` from the specified `urlRequest` for
  235. /// uploading the `data`.
  236. ///
  237. /// - parameter data: The data to upload.
  238. /// - parameter urlRequest: The URL request.
  239. ///
  240. /// - returns: The created upload `Request`.
  241. @discardableResult
  242. public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> Request {
  243. return SessionManager.default.upload(data, with: urlRequest)
  244. }
  245. // MARK: InputStream
  246. /// Creates an upload `Request` using the default `SessionManager` from the specified `method`, `urlString`
  247. /// and `headers` for uploading the `stream`.
  248. ///
  249. /// - parameter stream: The stream to upload.
  250. /// - parameter urlString: The URL string.
  251. /// - parameter method: The HTTP method.
  252. /// - parameter headers: The HTTP headers. `nil` by default.
  253. ///
  254. /// - returns: The created upload `Request`.
  255. @discardableResult
  256. public func upload(
  257. _ stream: InputStream,
  258. to urlString: URLStringConvertible,
  259. withMethod method: HTTPMethod,
  260. headers: [String: String]? = nil)
  261. -> Request
  262. {
  263. return SessionManager.default.upload(stream, to: urlString, withMethod: method, headers: headers)
  264. }
  265. /// Creates an upload `Request` using the default `SessionManager` from the specified `urlRequest` for
  266. /// uploading the `stream`.
  267. ///
  268. /// - parameter urlRequest: The URL request.
  269. /// - parameter stream: The stream to upload.
  270. ///
  271. /// - returns: The created upload `Request`.
  272. @discardableResult
  273. public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> Request {
  274. return SessionManager.default.upload(stream, with: urlRequest)
  275. }
  276. // MARK: MultipartFormData
  277. /// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls
  278. /// `encodingCompletion` with new upload `Request` using the `method`, `urlString` and `headers`.
  279. ///
  280. /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
  281. /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
  282. /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
  283. /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
  284. /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
  285. /// used for larger payloads such as video content.
  286. ///
  287. /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
  288. /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
  289. /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
  290. /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
  291. /// technique was used.
  292. ///
  293. /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
  294. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
  295. /// `multipartFormDataEncodingMemoryThreshold` by default.
  296. /// - parameter urlString: The URL string.
  297. /// - parameter method: The HTTP method.
  298. /// - parameter headers: The HTTP headers. `nil` by default.
  299. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
  300. public func upload(
  301. multipartFormData: (MultipartFormData) -> Void,
  302. usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
  303. to urlString: URLStringConvertible,
  304. withMethod method: HTTPMethod,
  305. headers: [String: String]? = nil,
  306. encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
  307. {
  308. return SessionManager.default.upload(
  309. multipartFormData: multipartFormData,
  310. usingThreshold: encodingMemoryThreshold,
  311. to: urlString,
  312. withMethod: method,
  313. headers: headers,
  314. encodingCompletion: encodingCompletion
  315. )
  316. }
  317. /// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and
  318. /// calls `encodingCompletion` with new upload `Request` using the `urlRequest`.
  319. ///
  320. /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
  321. /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
  322. /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
  323. /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
  324. /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
  325. /// used for larger payloads such as video content.
  326. ///
  327. /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
  328. /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
  329. /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
  330. /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
  331. /// technique was used.
  332. ///
  333. /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
  334. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
  335. /// `multipartFormDataEncodingMemoryThreshold` by default.
  336. /// - parameter urlRequest: The URL request.
  337. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
  338. public func upload(
  339. multipartFormData: (MultipartFormData) -> Void,
  340. usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
  341. with urlRequest: URLRequestConvertible,
  342. encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
  343. {
  344. return SessionManager.default.upload(
  345. multipartFormData: multipartFormData,
  346. usingThreshold: encodingMemoryThreshold,
  347. with: urlRequest,
  348. encodingCompletion: encodingCompletion
  349. )
  350. }