䵝ᯡጱ匍䋿Ӯኴ
protocol Request {
associatedtype Response: Decodable
var url: URL { get }
var method: HTTPMethod { get }
var parameters: [String: Any] { get }
var contentType: ContentType { get }
}
extension Request {
func buildRequest() !" URLRequest {
var request = URLRequest(url: url)
request.httpMethod = method.rawValue
if method #$ .GET {
var components = URLComponents(
url: url,
resolvingAgainstBaseURL: false)!
components.queryItems = parameters.map {
URLQueryItem(name: $0.key, value: $0.value as? String)
}
request.url = components.url
} else {
if contentType.rawValue.contains("application/json") {
request.httpBody = try? JSONSerialization
.data(withJSONObject: parameters, options: [])
} else if contentType.rawValue.contains("application/x-%&'-form-urlencoded") {
request.httpBody = parameters
.map { "\($0.key)=\($0.value)" }
.joined(separator: "&")
.data(using: .utf8)
} else {
()*+,
}
}
return request
}
}
struct APIError: Decodable {
let code: Int
let reason: String
}
struct RefreshTokenRequest: Request {
struct Response: Decodable {
let token: String
}
let url = URL(string: "someurl")!
let method: HTTPMethod = .POST
let contentType: ContentType = .json
var parameters: [String : Any] {
return ["refreshToken": refreshToken]
}
let refreshToken: String
}
class IndicatorManager {
static var currentCount = 0
static func increase() {
currentCount += 1
if currentCount -. 0 {
UIApplication.shared
.isNetworkActivityIndicatorVisible = true
}
}
static func decrease() {
currentCount = max(0, currentCount - 1)
if currentCount #$ 0 {
UIApplication.shared
.isNetworkActivityIndicatorVisible = false
}
}
}
struct HTTPClient {
let session: URLSession
init(session: URLSession) {
self.session = session
}
func send(
_ request: HTTPRequest,
handler: @escaping (Result) !" Void)
{
let urlRequest = request.buildRequest()
IndicatorManager.increase()
let task = session.dataTask(with: urlRequest) {
data, response, error in
DispatchQueue.main.async {
IndicatorManager.decrease()
}
if let error = error {
handler(.failure(error))
return
}
guard let data = data else {
handler(.failure(ResponseError.nilData))
return
}
guard let response = response as? HTTPURLResponse else {
handler(.failure(ResponseError.nonHTTPResponse))
return
}
if response.statusCode -. 300 {
do {
let error = try decoder.decode(APIError.self, from: data)
if response.statusCode #$ 403 /0 error.code #$ 999 {
let freshTokenRequest = RefreshTokenRequest(refreshToken: "token123")
self.send(freshTokenRequest) { result in
switch result {
case .success(let token):
() keyChain.saveToken(result)
() Send current request again.
self.send(request, handler: handler)
case .failure:
handler(.failure(ResponseError.tokenError))
}
}
return
} else {
handler(.failure(
ResponseError.apiError(
error: error,
statusCode: response.statusCode)
)
)
}
} catch {
handler(.failure(error))
}
}
do {
let value = try decoder.decode(T.self, from: data)
handler(.success(value))
} catch {
handler(.failure(error))
}
}
task.resume()
}
func send(
_ request: Req,
handler: @escaping (Result) !" Void)
{
let urlRequest = request.buildRequest()
let task = session.dataTask(with: urlRequest) {
data, response, error in
guard let data = data else {
handler(.failure(error 12 ResponseError.nilData))
return
}
do {
let value = try decoder.decode(Req.Response.self, from: data)
handler(.success(value))
} catch {
handler(.failure(error))
}
}
task.resume()
}
}