You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

574 lines
22 KiB

6 years ago
5 years ago
6 years ago
5 years ago
6 years ago
5 years ago
6 years ago
  1. //
  2. // Response.swift
  3. //
  4. // Copyright (c) 2014 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. /// Used to store all data associated with an non-serialized response of a data or upload request.
  26. public struct DefaultDataResponse {
  27. /// The URL request sent to the server.
  28. public let request: URLRequest?
  29. /// The server's response to the URL request.
  30. public let response: HTTPURLResponse?
  31. /// The data returned by the server.
  32. public let data: Data?
  33. /// The error encountered while executing or validating the request.
  34. public let error: Error?
  35. /// The timeline of the complete lifecycle of the request.
  36. public let timeline: Timeline
  37. var _metrics: AnyObject?
  38. /// Creates a `DefaultDataResponse` instance from the specified parameters.
  39. ///
  40. /// - Parameters:
  41. /// - request: The URL request sent to the server.
  42. /// - response: The server's response to the URL request.
  43. /// - data: The data returned by the server.
  44. /// - error: The error encountered while executing or validating the request.
  45. /// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
  46. /// - metrics: The task metrics containing the request / response statistics. `nil` by default.
  47. public init(
  48. request: URLRequest?,
  49. response: HTTPURLResponse?,
  50. data: Data?,
  51. error: Error?,
  52. timeline: Timeline = Timeline(),
  53. metrics: AnyObject? = nil)
  54. {
  55. self.request = request
  56. self.response = response
  57. self.data = data
  58. self.error = error
  59. self.timeline = timeline
  60. }
  61. }
  62. // MARK: -
  63. /// Used to store all data associated with a serialized response of a data or upload request.
  64. public struct DataResponse<Value> {
  65. /// The URL request sent to the server.
  66. public let request: URLRequest?
  67. /// The server's response to the URL request.
  68. public let response: HTTPURLResponse?
  69. /// The data returned by the server.
  70. public let data: Data?
  71. /// The result of response serialization.
  72. public let result: Result<Value>
  73. /// The timeline of the complete lifecycle of the request.
  74. public let timeline: Timeline
  75. /// Returns the associated value of the result if it is a success, `nil` otherwise.
  76. public var value: Value? { return result.value }
  77. /// Returns the associated error value if the result if it is a failure, `nil` otherwise.
  78. public var error: Error? { return result.error }
  79. var _metrics: AnyObject?
  80. /// Creates a `DataResponse` instance with the specified parameters derived from response serialization.
  81. ///
  82. /// - parameter request: The URL request sent to the server.
  83. /// - parameter response: The server's response to the URL request.
  84. /// - parameter data: The data returned by the server.
  85. /// - parameter result: The result of response serialization.
  86. /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
  87. ///
  88. /// - returns: The new `DataResponse` instance.
  89. public init(
  90. request: URLRequest?,
  91. response: HTTPURLResponse?,
  92. data: Data?,
  93. result: Result<Value>,
  94. timeline: Timeline = Timeline())
  95. {
  96. self.request = request
  97. self.response = response
  98. self.data = data
  99. self.result = result
  100. self.timeline = timeline
  101. }
  102. }
  103. // MARK: -
  104. extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible {
  105. /// The textual representation used when written to an output stream, which includes whether the result was a
  106. /// success or failure.
  107. public var description: String {
  108. return result.debugDescription
  109. }
  110. /// The debug textual representation used when written to an output stream, which includes the URL request, the URL
  111. /// response, the server data, the response serialization result and the timeline.
  112. public var debugDescription: String {
  113. let requestDescription = request.map { "\($0.httpMethod ?? "GET") \($0)"} ?? "nil"
  114. let requestBody = request?.httpBody.map { String(decoding: $0, as: UTF8.self) } ?? "None"
  115. let responseDescription = response.map { "\($0)" } ?? "nil"
  116. let responseBody = data.map { String(decoding: $0, as: UTF8.self) } ?? "None"
  117. return """
  118. [Request]: \(requestDescription)
  119. [Request Body]: \n\(requestBody)
  120. [Response]: \(responseDescription)
  121. [Response Body]: \n\(responseBody)
  122. [Result]: \(result)
  123. [Timeline]: \(timeline.debugDescription)
  124. """
  125. }
  126. }
  127. // MARK: -
  128. extension DataResponse {
  129. /// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped
  130. /// result value as a parameter.
  131. ///
  132. /// Use the `map` method with a closure that does not throw. For example:
  133. ///
  134. /// let possibleData: DataResponse<Data> = ...
  135. /// let possibleInt = possibleData.map { $0.count }
  136. ///
  137. /// - parameter transform: A closure that takes the success value of the instance's result.
  138. ///
  139. /// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's
  140. /// result is a failure, returns a response wrapping the same failure.
  141. public func map<T>(_ transform: (Value) -> T) -> DataResponse<T> {
  142. var response = DataResponse<T>(
  143. request: request,
  144. response: self.response,
  145. data: data,
  146. result: result.map(transform),
  147. timeline: timeline
  148. )
  149. response._metrics = _metrics
  150. return response
  151. }
  152. /// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result
  153. /// value as a parameter.
  154. ///
  155. /// Use the `flatMap` method with a closure that may throw an error. For example:
  156. ///
  157. /// let possibleData: DataResponse<Data> = ...
  158. /// let possibleObject = possibleData.flatMap {
  159. /// try JSONSerialization.jsonObject(with: $0)
  160. /// }
  161. ///
  162. /// - parameter transform: A closure that takes the success value of the instance's result.
  163. ///
  164. /// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's
  165. /// result is a failure, returns the same failure.
  166. public func flatMap<T>(_ transform: (Value) throws -> T) -> DataResponse<T> {
  167. var response = DataResponse<T>(
  168. request: request,
  169. response: self.response,
  170. data: data,
  171. result: result.flatMap(transform),
  172. timeline: timeline
  173. )
  174. response._metrics = _metrics
  175. return response
  176. }
  177. /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter.
  178. ///
  179. /// Use the `mapError` function with a closure that does not throw. For example:
  180. ///
  181. /// let possibleData: DataResponse<Data> = ...
  182. /// let withMyError = possibleData.mapError { MyError.error($0) }
  183. ///
  184. /// - Parameter transform: A closure that takes the error of the instance.
  185. /// - Returns: A `DataResponse` instance containing the result of the transform.
  186. public func mapError<E: Error>(_ transform: (Error) -> E) -> DataResponse {
  187. var response = DataResponse(
  188. request: request,
  189. response: self.response,
  190. data: data,
  191. result: result.mapError(transform),
  192. timeline: timeline
  193. )
  194. response._metrics = _metrics
  195. return response
  196. }
  197. /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter.
  198. ///
  199. /// Use the `flatMapError` function with a closure that may throw an error. For example:
  200. ///
  201. /// let possibleData: DataResponse<Data> = ...
  202. /// let possibleObject = possibleData.flatMapError {
  203. /// try someFailableFunction(taking: $0)
  204. /// }
  205. ///
  206. /// - Parameter transform: A throwing closure that takes the error of the instance.
  207. ///
  208. /// - Returns: A `DataResponse` instance containing the result of the transform.
  209. public func flatMapError<E: Error>(_ transform: (Error) throws -> E) -> DataResponse {
  210. var response = DataResponse(
  211. request: request,
  212. response: self.response,
  213. data: data,
  214. result: result.flatMapError(transform),
  215. timeline: timeline
  216. )
  217. response._metrics = _metrics
  218. return response
  219. }
  220. }
  221. // MARK: -
  222. /// Used to store all data associated with an non-serialized response of a download request.
  223. public struct DefaultDownloadResponse {
  224. /// The URL request sent to the server.
  225. public let request: URLRequest?
  226. /// The server's response to the URL request.
  227. public let response: HTTPURLResponse?
  228. /// The temporary destination URL of the data returned from the server.
  229. public let temporaryURL: URL?
  230. /// The final destination URL of the data returned from the server if it was moved.
  231. public let destinationURL: URL?
  232. /// The resume data generated if the request was cancelled.
  233. public let resumeData: Data?
  234. /// The error encountered while executing or validating the request.
  235. public let error: Error?
  236. /// The timeline of the complete lifecycle of the request.
  237. public let timeline: Timeline
  238. var _metrics: AnyObject?
  239. /// Creates a `DefaultDownloadResponse` instance from the specified parameters.
  240. ///
  241. /// - Parameters:
  242. /// - request: The URL request sent to the server.
  243. /// - response: The server's response to the URL request.
  244. /// - temporaryURL: The temporary destination URL of the data returned from the server.
  245. /// - destinationURL: The final destination URL of the data returned from the server if it was moved.
  246. /// - resumeData: The resume data generated if the request was cancelled.
  247. /// - error: The error encountered while executing or validating the request.
  248. /// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
  249. /// - metrics: The task metrics containing the request / response statistics. `nil` by default.
  250. public init(
  251. request: URLRequest?,
  252. response: HTTPURLResponse?,
  253. temporaryURL: URL?,
  254. destinationURL: URL?,
  255. resumeData: Data?,
  256. error: Error?,
  257. timeline: Timeline = Timeline(),
  258. metrics: AnyObject? = nil)
  259. {
  260. self.request = request
  261. self.response = response
  262. self.temporaryURL = temporaryURL
  263. self.destinationURL = destinationURL
  264. self.resumeData = resumeData
  265. self.error = error
  266. self.timeline = timeline
  267. }
  268. }
  269. // MARK: -
  270. /// Used to store all data associated with a serialized response of a download request.
  271. public struct DownloadResponse<Value> {
  272. /// The URL request sent to the server.
  273. public let request: URLRequest?
  274. /// The server's response to the URL request.
  275. public let response: HTTPURLResponse?
  276. /// The temporary destination URL of the data returned from the server.
  277. public let temporaryURL: URL?
  278. /// The final destination URL of the data returned from the server if it was moved.
  279. public let destinationURL: URL?
  280. /// The resume data generated if the request was cancelled.
  281. public let resumeData: Data?
  282. /// The result of response serialization.
  283. public let result: Result<Value>
  284. /// The timeline of the complete lifecycle of the request.
  285. public let timeline: Timeline
  286. /// Returns the associated value of the result if it is a success, `nil` otherwise.
  287. public var value: Value? { return result.value }
  288. /// Returns the associated error value if the result if it is a failure, `nil` otherwise.
  289. public var error: Error? { return result.error }
  290. var _metrics: AnyObject?
  291. /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization.
  292. ///
  293. /// - parameter request: The URL request sent to the server.
  294. /// - parameter response: The server's response to the URL request.
  295. /// - parameter temporaryURL: The temporary destination URL of the data returned from the server.
  296. /// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved.
  297. /// - parameter resumeData: The resume data generated if the request was cancelled.
  298. /// - parameter result: The result of response serialization.
  299. /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
  300. ///
  301. /// - returns: The new `DownloadResponse` instance.
  302. public init(
  303. request: URLRequest?,
  304. response: HTTPURLResponse?,
  305. temporaryURL: URL?,
  306. destinationURL: URL?,
  307. resumeData: Data?,
  308. result: Result<Value>,
  309. timeline: Timeline = Timeline())
  310. {
  311. self.request = request
  312. self.response = response
  313. self.temporaryURL = temporaryURL
  314. self.destinationURL = destinationURL
  315. self.resumeData = resumeData
  316. self.result = result
  317. self.timeline = timeline
  318. }
  319. }
  320. // MARK: -
  321. extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible {
  322. /// The textual representation used when written to an output stream, which includes whether the result was a
  323. /// success or failure.
  324. public var description: String {
  325. return result.debugDescription
  326. }
  327. /// The debug textual representation used when written to an output stream, which includes the URL request, the URL
  328. /// response, the temporary and destination URLs, the resume data, the response serialization result and the
  329. /// timeline.
  330. public var debugDescription: String {
  331. let requestDescription = request.map { "\($0.httpMethod ?? "GET") \($0)"} ?? "nil"
  332. let requestBody = request?.httpBody.map { String(decoding: $0, as: UTF8.self) } ?? "None"
  333. let responseDescription = response.map { "\($0)" } ?? "nil"
  334. return """
  335. [Request]: \(requestDescription)
  336. [Request Body]: \n\(requestBody)
  337. [Response]: \(responseDescription)
  338. [TemporaryURL]: \(temporaryURL?.path ?? "nil")
  339. [DestinationURL]: \(destinationURL?.path ?? "nil")
  340. [ResumeData]: \(resumeData?.count ?? 0) bytes
  341. [Result]: \(result)
  342. [Timeline]: \(timeline.debugDescription)
  343. """
  344. }
  345. }
  346. // MARK: -
  347. extension DownloadResponse {
  348. /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped
  349. /// result value as a parameter.
  350. ///
  351. /// Use the `map` method with a closure that does not throw. For example:
  352. ///
  353. /// let possibleData: DownloadResponse<Data> = ...
  354. /// let possibleInt = possibleData.map { $0.count }
  355. ///
  356. /// - parameter transform: A closure that takes the success value of the instance's result.
  357. ///
  358. /// - returns: A `DownloadResponse` whose result wraps the value returned by the given closure. If this instance's
  359. /// result is a failure, returns a response wrapping the same failure.
  360. public func map<T>(_ transform: (Value) -> T) -> DownloadResponse<T> {
  361. var response = DownloadResponse<T>(
  362. request: request,
  363. response: self.response,
  364. temporaryURL: temporaryURL,
  365. destinationURL: destinationURL,
  366. resumeData: resumeData,
  367. result: result.map(transform),
  368. timeline: timeline
  369. )
  370. response._metrics = _metrics
  371. return response
  372. }
  373. /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped
  374. /// result value as a parameter.
  375. ///
  376. /// Use the `flatMap` method with a closure that may throw an error. For example:
  377. ///
  378. /// let possibleData: DownloadResponse<Data> = ...
  379. /// let possibleObject = possibleData.flatMap {
  380. /// try JSONSerialization.jsonObject(with: $0)
  381. /// }
  382. ///
  383. /// - parameter transform: A closure that takes the success value of the instance's result.
  384. ///
  385. /// - returns: A success or failure `DownloadResponse` depending on the result of the given closure. If this
  386. /// instance's result is a failure, returns the same failure.
  387. public func flatMap<T>(_ transform: (Value) throws -> T) -> DownloadResponse<T> {
  388. var response = DownloadResponse<T>(
  389. request: request,
  390. response: self.response,
  391. temporaryURL: temporaryURL,
  392. destinationURL: destinationURL,
  393. resumeData: resumeData,
  394. result: result.flatMap(transform),
  395. timeline: timeline
  396. )
  397. response._metrics = _metrics
  398. return response
  399. }
  400. /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter.
  401. ///
  402. /// Use the `mapError` function with a closure that does not throw. For example:
  403. ///
  404. /// let possibleData: DownloadResponse<Data> = ...
  405. /// let withMyError = possibleData.mapError { MyError.error($0) }
  406. ///
  407. /// - Parameter transform: A closure that takes the error of the instance.
  408. /// - Returns: A `DownloadResponse` instance containing the result of the transform.
  409. public func mapError<E: Error>(_ transform: (Error) -> E) -> DownloadResponse {
  410. var response = DownloadResponse(
  411. request: request,
  412. response: self.response,
  413. temporaryURL: temporaryURL,
  414. destinationURL: destinationURL,
  415. resumeData: resumeData,
  416. result: result.mapError(transform),
  417. timeline: timeline
  418. )
  419. response._metrics = _metrics
  420. return response
  421. }
  422. /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter.
  423. ///
  424. /// Use the `flatMapError` function with a closure that may throw an error. For example:
  425. ///
  426. /// let possibleData: DownloadResponse<Data> = ...
  427. /// let possibleObject = possibleData.flatMapError {
  428. /// try someFailableFunction(taking: $0)
  429. /// }
  430. ///
  431. /// - Parameter transform: A throwing closure that takes the error of the instance.
  432. ///
  433. /// - Returns: A `DownloadResponse` instance containing the result of the transform.
  434. public func flatMapError<E: Error>(_ transform: (Error) throws -> E) -> DownloadResponse {
  435. var response = DownloadResponse(
  436. request: request,
  437. response: self.response,
  438. temporaryURL: temporaryURL,
  439. destinationURL: destinationURL,
  440. resumeData: resumeData,
  441. result: result.flatMapError(transform),
  442. timeline: timeline
  443. )
  444. response._metrics = _metrics
  445. return response
  446. }
  447. }
  448. // MARK: -
  449. protocol Response {
  450. /// The task metrics containing the request / response statistics.
  451. var _metrics: AnyObject? { get set }
  452. mutating func add(_ metrics: AnyObject?)
  453. }
  454. extension Response {
  455. mutating func add(_ metrics: AnyObject?) {
  456. #if !os(watchOS)
  457. guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return }
  458. guard let metrics = metrics as? URLSessionTaskMetrics else { return }
  459. _metrics = metrics
  460. #endif
  461. }
  462. }
  463. // MARK: -
  464. @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
  465. extension DefaultDataResponse: Response {
  466. #if !os(watchOS)
  467. /// The task metrics containing the request / response statistics.
  468. public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
  469. #endif
  470. }
  471. @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
  472. extension DataResponse: Response {
  473. #if !os(watchOS)
  474. /// The task metrics containing the request / response statistics.
  475. public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
  476. #endif
  477. }
  478. @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
  479. extension DefaultDownloadResponse: Response {
  480. #if !os(watchOS)
  481. /// The task metrics containing the request / response statistics.
  482. public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
  483. #endif
  484. }
  485. @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
  486. extension DownloadResponse: Response {
  487. #if !os(watchOS)
  488. /// The task metrics containing the request / response statistics.
  489. public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
  490. #endif
  491. }