Alamofire.swift 19 KB

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