Alamofire.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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. // MARK: -
  50. /// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests.
  51. public protocol URLRequestConvertible {
  52. /// The URL request.
  53. var urlRequest: URLRequest { get }
  54. }
  55. extension URLRequest: URLRequestConvertible {
  56. /// The URL request.
  57. public var urlRequest: URLRequest { return self }
  58. }
  59. // MARK: -
  60. extension URLRequest {
  61. /// Creates an instance with the specified `method`, `urlString` and `headers`.
  62. ///
  63. /// - parameter urlString: The URL string.
  64. /// - parameter method: The HTTP method.
  65. /// - parameter headers: The HTTP headers. `nil` by default.
  66. ///
  67. /// - returns: The new `URLRequest` instance.
  68. public init(urlString: URLStringConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) {
  69. self.init(url: URL(string: urlString.urlString)!)
  70. if let request = urlString as? URLRequest { self = request }
  71. httpMethod = method.rawValue
  72. if let headers = headers {
  73. for (headerField, headerValue) in headers {
  74. setValue(headerValue, forHTTPHeaderField: headerField)
  75. }
  76. }
  77. }
  78. func adapt(using adapter: RequestAdapter?) -> URLRequest {
  79. guard let adapter = adapter else { return self }
  80. return adapter.adapt(self)
  81. }
  82. }
  83. // MARK: - Data Request
  84. /// Creates a `DataRequest` 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. `.get` by default.
  89. /// - parameter parameters: The parameters. `nil` by default.
  90. /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
  91. /// - parameter headers: The HTTP headers. `nil` by default.
  92. ///
  93. /// - returns: The created `DataRequest`.
  94. @discardableResult
  95. public func request(
  96. _ urlString: URLStringConvertible,
  97. method: HTTPMethod = .get,
  98. parameters: Parameters? = nil,
  99. encoding: ParameterEncoding = URLEncoding.default,
  100. headers: HTTPHeaders? = nil)
  101. -> DataRequest
  102. {
  103. return SessionManager.default.request(
  104. urlString,
  105. method: method,
  106. parameters: parameters,
  107. encoding: encoding,
  108. headers: headers
  109. )
  110. }
  111. /// Creates a `DataRequest` 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 `DataRequest`.
  117. @discardableResult
  118. public func request(resource urlRequest: URLRequestConvertible) -> DataRequest {
  119. return SessionManager.default.request(resource: urlRequest)
  120. }
  121. // MARK: - Download Request
  122. // MARK: URL Request
  123. /// Creates a `DownloadRequest` 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. /// If `destination` is not specified, the contents will remain in the temporary location determined by the
  127. /// underlying URL session.
  128. ///
  129. /// - parameter urlString: The URL string.
  130. /// - parameter method: The HTTP method. `.get` by default.
  131. /// - parameter parameters: The parameters. `nil` by default.
  132. /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
  133. /// - parameter headers: The HTTP headers. `nil` by default.
  134. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
  135. ///
  136. /// - returns: The created `DownloadRequest`.
  137. @discardableResult
  138. public func download(
  139. _ urlString: URLStringConvertible,
  140. method: HTTPMethod = .get,
  141. parameters: Parameters? = nil,
  142. encoding: ParameterEncoding = URLEncoding.default,
  143. headers: HTTPHeaders? = nil,
  144. to destination: DownloadRequest.DownloadFileDestination? = nil)
  145. -> DownloadRequest
  146. {
  147. return SessionManager.default.download(
  148. urlString,
  149. method: method,
  150. parameters: parameters,
  151. encoding: encoding,
  152. headers: headers,
  153. to: destination
  154. )
  155. }
  156. /// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
  157. /// specified `urlRequest` and save them to the `destination`.
  158. ///
  159. /// If `destination` is not specified, the contents will remain in the temporary location determined by the
  160. /// underlying URL session.
  161. ///
  162. /// - parameter urlRequest: The URL request.
  163. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
  164. ///
  165. /// - returns: The created `DownloadRequest`.
  166. @discardableResult
  167. public func download(
  168. resource urlRequest: URLRequestConvertible,
  169. to destination: DownloadRequest.DownloadFileDestination? = nil)
  170. -> DownloadRequest
  171. {
  172. return SessionManager.default.download(resource: urlRequest, to: destination)
  173. }
  174. // MARK: Resume Data
  175. /// Creates a `DownloadRequest` using the default `SessionManager` from the `resumeData` produced from a
  176. /// previous request cancellation to retrieve the contents of the original request and save them to the `destination`.
  177. ///
  178. /// If `destination` is not specified, the contents will remain in the temporary location determined by the
  179. /// underlying URL session.
  180. ///
  181. /// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask`
  182. /// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional
  183. /// information.
  184. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
  185. ///
  186. /// - returns: The created `DownloadRequest`.
  187. @discardableResult
  188. public func download(
  189. resourceWithin resumeData: Data,
  190. to destination: DownloadRequest.DownloadFileDestination? = nil)
  191. -> DownloadRequest
  192. {
  193. return SessionManager.default.download(resourceWithin: resumeData, to: destination)
  194. }
  195. // MARK: - Upload Request
  196. // MARK: File
  197. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `method`, `urlString`
  198. /// and `headers` for uploading the `file`.
  199. ///
  200. /// - parameter file: The file to upload.
  201. /// - parameter urlString: The URL string.
  202. /// - parameter method: The HTTP method. `.post` by default.
  203. /// - parameter headers: The HTTP headers. `nil` by default.
  204. ///
  205. /// - returns: The created `UploadRequest`.
  206. @discardableResult
  207. public func upload(
  208. _ fileURL: URL,
  209. to urlString: URLStringConvertible,
  210. method: HTTPMethod = .post,
  211. headers: HTTPHeaders? = nil)
  212. -> UploadRequest
  213. {
  214. return SessionManager.default.upload(fileURL, to: urlString, method: method, headers: headers)
  215. }
  216. /// Creates a `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
  217. /// uploading the `file`.
  218. ///
  219. /// - parameter file: The file to upload.
  220. /// - parameter urlRequest: The URL request.
  221. ///
  222. /// - returns: The created `UploadRequest`.
  223. @discardableResult
  224. public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest {
  225. return SessionManager.default.upload(fileURL, with: urlRequest)
  226. }
  227. // MARK: Data
  228. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `method`, `urlString`
  229. /// and `headers` for uploading the `data`.
  230. ///
  231. /// - parameter data: The data to upload.
  232. /// - parameter urlString: The URL string.
  233. /// - parameter method: The HTTP method. `.post` by default.
  234. /// - parameter headers: The HTTP headers. `nil` by default.
  235. ///
  236. /// - returns: The created `UploadRequest`.
  237. @discardableResult
  238. public func upload(
  239. _ data: Data,
  240. to urlString: URLStringConvertible,
  241. method: HTTPMethod = .post,
  242. headers: HTTPHeaders? = nil)
  243. -> UploadRequest
  244. {
  245. return SessionManager.default.upload(data, to: urlString, method: method, headers: headers)
  246. }
  247. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
  248. /// uploading the `data`.
  249. ///
  250. /// - parameter data: The data to upload.
  251. /// - parameter urlRequest: The URL request.
  252. ///
  253. /// - returns: The created `UploadRequest`.
  254. @discardableResult
  255. public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
  256. return SessionManager.default.upload(data, with: urlRequest)
  257. }
  258. // MARK: InputStream
  259. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `method`, `urlString`
  260. /// and `headers` for uploading the `stream`.
  261. ///
  262. /// - parameter stream: The stream to upload.
  263. /// - parameter urlString: The URL string.
  264. /// - parameter method: The HTTP method. `.post` by default.
  265. /// - parameter headers: The HTTP headers. `nil` by default.
  266. ///
  267. /// - returns: The created `UploadRequest`.
  268. @discardableResult
  269. public func upload(
  270. _ stream: InputStream,
  271. to urlString: URLStringConvertible,
  272. method: HTTPMethod = .post,
  273. headers: HTTPHeaders? = nil)
  274. -> UploadRequest
  275. {
  276. return SessionManager.default.upload(stream, to: urlString, method: method, headers: headers)
  277. }
  278. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
  279. /// uploading the `stream`.
  280. ///
  281. /// - parameter urlRequest: The URL request.
  282. /// - parameter stream: The stream to upload.
  283. ///
  284. /// - returns: The created `UploadRequest`.
  285. @discardableResult
  286. public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest {
  287. return SessionManager.default.upload(stream, with: urlRequest)
  288. }
  289. // MARK: MultipartFormData
  290. /// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls
  291. /// `encodingCompletion` with new `UploadRequest` using the `method`, `urlString` and `headers`.
  292. ///
  293. /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
  294. /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
  295. /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
  296. /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
  297. /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
  298. /// used for larger payloads such as video content.
  299. ///
  300. /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
  301. /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
  302. /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
  303. /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
  304. /// technique was used.
  305. ///
  306. /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
  307. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
  308. /// `multipartFormDataEncodingMemoryThreshold` by default.
  309. /// - parameter urlString: The URL string.
  310. /// - parameter method: The HTTP method. `.post` by default.
  311. /// - parameter headers: The HTTP headers. `nil` by default.
  312. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
  313. public func upload(
  314. multipartFormData: @escaping (MultipartFormData) -> Void,
  315. usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
  316. to urlString: URLStringConvertible,
  317. method: HTTPMethod = .post,
  318. headers: HTTPHeaders? = nil,
  319. encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
  320. {
  321. return SessionManager.default.upload(
  322. multipartFormData: multipartFormData,
  323. usingThreshold: encodingMemoryThreshold,
  324. to: urlString,
  325. method: method,
  326. headers: headers,
  327. encodingCompletion: encodingCompletion
  328. )
  329. }
  330. /// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and
  331. /// calls `encodingCompletion` with new `UploadRequest` using the `urlRequest`.
  332. ///
  333. /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
  334. /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
  335. /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
  336. /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
  337. /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
  338. /// used for larger payloads such as video content.
  339. ///
  340. /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
  341. /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
  342. /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
  343. /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
  344. /// technique was used.
  345. ///
  346. /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
  347. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
  348. /// `multipartFormDataEncodingMemoryThreshold` by default.
  349. /// - parameter urlRequest: The URL request.
  350. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
  351. public func upload(
  352. multipartFormData: @escaping (MultipartFormData) -> Void,
  353. usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
  354. with urlRequest: URLRequestConvertible,
  355. encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
  356. {
  357. return SessionManager.default.upload(
  358. multipartFormData: multipartFormData,
  359. usingThreshold: encodingMemoryThreshold,
  360. with: urlRequest,
  361. encodingCompletion: encodingCompletion
  362. )
  363. }
  364. #if !os(watchOS)
  365. // MARK: - Stream Request
  366. // MARK: Hostname and Port
  367. /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `hostname`
  368. /// and `port`.
  369. ///
  370. /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  371. ///
  372. /// - parameter hostName: The hostname of the server to connect to.
  373. /// - parameter port: The port of the server to connect to.
  374. ///
  375. /// - returns: The created `StreamRequest`.
  376. @discardableResult
  377. public func stream(withHostName hostName: String, port: Int) -> StreamRequest {
  378. return SessionManager.default.stream(withHostName: hostName, port: port)
  379. }
  380. // MARK: NetService
  381. /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `netService`.
  382. ///
  383. /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  384. ///
  385. /// - parameter netService: The net service used to identify the endpoint.
  386. ///
  387. /// - returns: The created `StreamRequest`.
  388. @discardableResult
  389. public func stream(with netService: NetService) -> StreamRequest {
  390. return SessionManager.default.stream(with: netService)
  391. }
  392. #endif