123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- import Foundation
- /// Used for stubbing responses.
- public enum EndpointSampleResponse {
- /// The network returned a response, including status code and data.
- case networkResponse(Int, Data)
- /// The network returned response which can be fully customized.
- case response(HTTPURLResponse, Data)
- /// The network failed to send the request, or failed to retrieve a response (eg a timeout).
- case networkError(NSError)
- }
- /// Class for reifying a target of the `Target` enum unto a concrete `Endpoint`.
- /// - Note: As of Moya 11.0.0 Endpoint is no longer generic.
- /// Existing code should work as is after removing the generic.
- /// See #1529 and #1524 for the discussion.
- open class Endpoint {
- public typealias SampleResponseClosure = () -> EndpointSampleResponse
- /// A string representation of the URL for the request.
- public let url: String
- /// A closure responsible for returning an `EndpointSampleResponse`.
- public let sampleResponseClosure: SampleResponseClosure
- /// The HTTP method for the request.
- public let method: Moya.Method
- /// The `Task` for the request.
- public let task: Task
- /// The HTTP header fields for the request.
- public let httpHeaderFields: [String: String]?
- public init(url: String,
- sampleResponseClosure: @escaping SampleResponseClosure,
- method: Moya.Method,
- task: Task,
- httpHeaderFields: [String: String]?) {
- self.url = url
- self.sampleResponseClosure = sampleResponseClosure
- self.method = method
- self.task = task
- self.httpHeaderFields = httpHeaderFields
- }
- /// Convenience method for creating a new `Endpoint` with the same properties as the receiver, but with added HTTP header fields.
- open func adding(newHTTPHeaderFields: [String: String]) -> Endpoint {
- return Endpoint(url: url, sampleResponseClosure: sampleResponseClosure, method: method, task: task, httpHeaderFields: add(httpHeaderFields: newHTTPHeaderFields))
- }
- /// Convenience method for creating a new `Endpoint` with the same properties as the receiver, but with replaced `task` parameter.
- open func replacing(task: Task) -> Endpoint {
- return Endpoint(url: url, sampleResponseClosure: sampleResponseClosure, method: method, task: task, httpHeaderFields: httpHeaderFields)
- }
- fileprivate func add(httpHeaderFields headers: [String: String]?) -> [String: String]? {
- guard let unwrappedHeaders = headers, unwrappedHeaders.isEmpty == false else {
- return self.httpHeaderFields
- }
- var newHTTPHeaderFields = self.httpHeaderFields ?? [:]
- unwrappedHeaders.forEach { key, value in
- newHTTPHeaderFields[key] = value
- }
- return newHTTPHeaderFields
- }
- }
- /// Extension for converting an `Endpoint` into a `URLRequest`.
- public extension Endpoint {
- // swiftlint:disable cyclomatic_complexity
- /// Returns the `Endpoint` converted to a `URLRequest` if valid. Throws an error otherwise.
- func urlRequest() throws -> URLRequest {
- guard let requestURL = Foundation.URL(string: url) else {
- throw MoyaError.requestMapping(url)
- }
- var request = URLRequest(url: requestURL)
- request.httpMethod = method.rawValue
- request.allHTTPHeaderFields = httpHeaderFields
- switch task {
- case .requestPlain, .uploadFile, .uploadMultipart, .downloadDestination:
- return request
- case .requestData(let data):
- request.httpBody = data
- return request
- case let .requestJSONEncodable(encodable):
- return try request.encoded(encodable: encodable)
- case let .requestCustomJSONEncodable(encodable, encoder: encoder):
- return try request.encoded(encodable: encodable, encoder: encoder)
- case let .requestParameters(parameters, parameterEncoding):
- return try request.encoded(parameters: parameters, parameterEncoding: parameterEncoding)
- case let .uploadCompositeMultipart(_, urlParameters):
- let parameterEncoding = URLEncoding(destination: .queryString)
- return try request.encoded(parameters: urlParameters, parameterEncoding: parameterEncoding)
- case let .downloadParameters(parameters, parameterEncoding, _):
- return try request.encoded(parameters: parameters, parameterEncoding: parameterEncoding)
- case let .requestCompositeData(bodyData: bodyData, urlParameters: urlParameters):
- request.httpBody = bodyData
- let parameterEncoding = URLEncoding(destination: .queryString)
- return try request.encoded(parameters: urlParameters, parameterEncoding: parameterEncoding)
- case let .requestCompositeParameters(bodyParameters: bodyParameters, bodyEncoding: bodyParameterEncoding, urlParameters: urlParameters):
- if let bodyParameterEncoding = bodyParameterEncoding as? URLEncoding, bodyParameterEncoding.destination != .httpBody {
- fatalError("Only URLEncoding that `bodyEncoding` accepts is URLEncoding.httpBody. Others like `default`, `queryString` or `methodDependent` are prohibited - if you want to use them, add your parameters to `urlParameters` instead.")
- }
- let bodyfulRequest = try request.encoded(parameters: bodyParameters, parameterEncoding: bodyParameterEncoding)
- let urlEncoding = URLEncoding(destination: .queryString)
- return try bodyfulRequest.encoded(parameters: urlParameters, parameterEncoding: urlEncoding)
- }
- }
- // swiftlint:enable cyclomatic_complexity
- }
- /// Required for using `Endpoint` as a key type in a `Dictionary`.
- extension Endpoint: Equatable, Hashable {
- public func hash(into hasher: inout Hasher) {
- guard let request = try? urlRequest() else {
- hasher.combine(url)
- return
- }
- hasher.combine(request)
- }
- /// Note: If both Endpoints fail to produce a URLRequest the comparison will
- /// fall back to comparing each Endpoint's hashValue.
- public static func == (lhs: Endpoint, rhs: Endpoint) -> Bool {
- let lhsRequest = try? lhs.urlRequest()
- let rhsRequest = try? rhs.urlRequest()
- if lhsRequest != nil, rhsRequest == nil { return false }
- if lhsRequest == nil, rhsRequest != nil { return false }
- if lhsRequest == nil, rhsRequest == nil { return lhs.hashValue == rhs.hashValue }
- return (lhsRequest == rhsRequest)
- }
- }
|