forked from kudoleh/iOS-Clean-Architecture-MVVM
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEndpoint.swift
executable file
·147 lines (124 loc) · 5.06 KB
/
Endpoint.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//
// Endpoint.swift
// ExampleMVVM
//
// Created by Oleh Kudinov on 01.10.18.
//
import Foundation
public enum HTTPMethodType: String {
case get = "GET"
case head = "HEAD"
case post = "POST"
case put = "PUT"
case delete = "DELETE"
}
public enum BodyEncoding {
case jsonSerializationData
case stringEncodingAscii
}
public class Endpoint<R>: ResponseRequestable {
public typealias Response = R
public var path: String
public var isFullPath: Bool
public var method: HTTPMethodType
public var headerParamaters: [String: String]
public var queryParametersEncodable: Encodable? = nil
public var queryParameters: [String: Any]
public var bodyParamatersEncodable: Encodable? = nil
public var bodyParamaters: [String: Any]
public var bodyEncoding: BodyEncoding
public var responseDecoder: ResponseDecoder
init(path: String,
isFullPath: Bool = false,
method: HTTPMethodType,
headerParamaters: [String: String] = [:],
queryParametersEncodable: Encodable? = nil,
queryParameters: [String: Any] = [:],
bodyParamatersEncodable: Encodable? = nil,
bodyParamaters: [String: Any] = [:],
bodyEncoding: BodyEncoding = .jsonSerializationData,
responseDecoder: ResponseDecoder = JSONResponseDecoder()) {
self.path = path
self.isFullPath = isFullPath
self.method = method
self.headerParamaters = headerParamaters
self.queryParametersEncodable = queryParametersEncodable
self.queryParameters = queryParameters
self.bodyParamatersEncodable = bodyParamatersEncodable
self.bodyParamaters = bodyParamaters
self.bodyEncoding = bodyEncoding
self.responseDecoder = responseDecoder
}
}
public protocol Requestable {
var path: String { get }
var isFullPath: Bool { get }
var method: HTTPMethodType { get }
var headerParamaters: [String: String] { get }
var queryParametersEncodable: Encodable? { get }
var queryParameters: [String: Any] { get }
var bodyParamatersEncodable: Encodable? { get }
var bodyParamaters: [String: Any] { get }
var bodyEncoding: BodyEncoding { get }
func urlRequest(with networkConfig: NetworkConfigurable) throws -> URLRequest
}
public protocol ResponseRequestable: Requestable {
associatedtype Response
var responseDecoder: ResponseDecoder { get }
}
enum RequestGenerationError: Error {
case components
}
extension Requestable {
func url(with config: NetworkConfigurable) throws -> URL {
let baseURL = config.baseURL.absoluteString.last != "/" ? config.baseURL.absoluteString + "/" : config.baseURL.absoluteString
let endpoint = isFullPath ? path : baseURL.appending(path)
guard var urlComponents = URLComponents(string: endpoint) else { throw RequestGenerationError.components }
var urlQueryItems = [URLQueryItem]()
let queryParameters = try queryParametersEncodable?.toDictionary() ?? self.queryParameters
queryParameters.forEach {
urlQueryItems.append(URLQueryItem(name: $0.key, value: "\($0.value)"))
}
config.queryParameters.forEach {
urlQueryItems.append(URLQueryItem(name: $0.key, value: $0.value))
}
urlComponents.queryItems = !urlQueryItems.isEmpty ? urlQueryItems : nil
guard let url = urlComponents.url else { throw RequestGenerationError.components }
return url
}
public func urlRequest(with config: NetworkConfigurable) throws -> URLRequest {
let url = try self.url(with: config)
var urlRequest = URLRequest(url: url)
var allHeaders: [String: String] = config.headers
headerParamaters.forEach { allHeaders.updateValue($1, forKey: $0) }
let bodyParamaters = try bodyParamatersEncodable?.toDictionary() ?? self.bodyParamaters
if !bodyParamaters.isEmpty {
urlRequest.httpBody = encodeBody(bodyParamaters: bodyParamaters, bodyEncoding: bodyEncoding)
}
urlRequest.httpMethod = method.rawValue
urlRequest.allHTTPHeaderFields = allHeaders
return urlRequest
}
private func encodeBody(bodyParamaters: [String: Any], bodyEncoding: BodyEncoding) -> Data? {
switch bodyEncoding {
case .jsonSerializationData:
return try? JSONSerialization.data(withJSONObject: bodyParamaters)
case .stringEncodingAscii:
return bodyParamaters.queryString.data(using: String.Encoding.ascii, allowLossyConversion: true)
}
}
}
private extension Dictionary {
var queryString: String {
return self.map { "\($0.key)=\($0.value)" }
.joined(separator: "&")
.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed) ?? ""
}
}
private extension Encodable {
func toDictionary() throws -> [String: Any]? {
let data = try JSONEncoder().encode(self)
let josnData = try JSONSerialization.jsonObject(with: data)
return josnData as? [String : Any]
}
}