Alamofire.swift 17 KB

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