Skip to content

Instantly share code, notes, and snippets.

@CenoX
Created March 2, 2020 10:22
Show Gist options
  • Save CenoX/56780cd4ef7722ca8cc1bae4b4f511b0 to your computer and use it in GitHub Desktop.
Save CenoX/56780cd4ef7722ca8cc1bae4b4f511b0 to your computer and use it in GitHub Desktop.
본인이 쓰는 APIManager 구조
typealias APICompletionHandler<T: Decodable> = ((Result<T, Error>) -> Void)
typealias ResultT<T> = (Result<T, Error>)
final class APIManager {
public static let shared = APIManager()
// TEMP URL!!!
private let stringURL = "https://api.cenox.co"
private let baseURL: URL
private init() {
XCGLogger.default.debug("APIManager has initialized.")
baseURL = URL(string: stringURL)!
}
}
// MARK: - APIAppProtocol
extension APIManager: APIAppProtocol {
func getAppVersion<T: Decodable>(with currentVersion: String, _ completion: @escaping APICompletionHandler<T>) {
let url = baseURL.appendingPathComponent("checkupdate")
var component = URLComponents(url: url, resolvingAgainstBaseURL: true)
component?.queryItems = [URLQueryItem(name: "version", value: currentVersion)]
var request = URLRequest(url: component!.url!)
request.httpMethod = "GET"
process(request, passedHandler: completion)
}
}
// MARK: - Process API
extension APIManager {
private func process<T: Decodable>(_ request: URLRequest, passedHandler completion: @escaping APICompletionHandler<T>) {
XCGLogger.default.debug("[\(request.url?.absoluteString ?? "Can't parse url")]")
URLSession.shared.dataTask(with: request) { data, response, error in
// MARK: - Request error handling
if let error = error {
XCGLogger.default.error("API Request got error. \(error)")
completion(.failure(error))
return
}
// MARK: - API Response Handling
if let response = response as? HTTPURLResponse {
XCGLogger.default.debug("[\(request.url?.path ?? "Can't parse url path")] Response HTTP Code -> \(response.statusCode)")
// 204 is not error!
if response.statusCode == 204 {
let dummy = try! NoValueFromAPI(message: "No Content").encode()
let data = try! JSONDecoder().decode(T.self, from: dummy)
XCGLogger.default.debug("204 returned. passing value")
completion(.success(data))
return
}
let APIError: APIRequestError
let errorMessage = self.parseJSONError(data: data)
if response.statusCode != 200 {
XCGLogger.default.error("parsed JSON Error -> \(errorMessage)")
}
switch response.statusCode {
case 400:
APIError = .badRequest(message: errorMessage)
completion(.failure(APIError))
return
case 403:
APIError = .forbidden(message: errorMessage)
completion(.failure(APIError))
return
case 404:
APIError = .notFound(message: errorMessage)
completion(.failure(APIError))
return
case 405:
APIError = .methodNotAllowed(message: errorMessage)
completion(.failure(APIError))
return
case 409:
APIError = .alreadyExists(message: errorMessage)
completion(.failure(APIError))
return
case 500:
APIError = .internalServerError(message: errorMessage)
completion(.failure(APIError))
return
case 504:
APIError = .gatewayTimeout(message: errorMessage)
completion(.failure(APIError))
return
default:
APIError = .unknown
break
}
}
// MARK: -
guard let data = data else {
completion(.failure(APIRequestError.unparseableData))
return
}
let str = String(data: data, encoding: .utf8) ?? "Couldn't parse"
XCGLogger.default.debug("Literal Response -> \(str)")
do {
let result = try Result { try JSONDecoder().decode(T.self, from: data) }.get()
completion(.success(result))
} catch {
completion(.failure(error))
}
}.resume()
}
private func parseJSONError(data: Data?) -> String {
if let data = data, let str = String(data: data, encoding: .utf8) {
return str
}
return "Can't parse json error message"
}
}
import Foundation
protocol APIAppProtocol {
/**
서버에 앱 버전을 요청합니다.
예상 리스폰스: `APIAppVersion`
- Parameter currentVersion: 현재 앱의 버전입니다. Info.plist 에서 `CFBundleShortVersionString` 값을 기준으로 합니다.
- Parameter completion: API Request가 종료되었을 때 불리는 콜백 함수입니다. 자세한 내용은 `APICompletionHandler`를 참고하세요.
*/
func getAppVersion<T: Decodable>(with currentVersion: String, _ completion: @escaping APICompletionHandler<T>)
}
struct APIAppVersion: Decodable {
var updateAvailable: Bool
var isEssential: Bool
private enum CodingKeys: String, CodingKey {
case updateAvailable = "update_available"
case isEssential
}
}
// MARK: - API
extension SplashViewController {
func checkVersion() {
APIManager.shared.getAppVersion(with: Bundle.main.appVersion) { [unowned self] (result: ResultT<APIAppVersion>) in
do {
let result = try result.get()
if result.updateAvailable {
self.presentAlert(isEssential: result.isEssential)
} else {
self.processApp()
}
} catch {
self.errorAlert(message: error.localizedDescription)
XCGLogger.default.error("Error while handling app version. \(error)")
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment