Created
March 2, 2020 10:22
-
-
Save CenoX/56780cd4ef7722ca8cc1bae4b4f511b0 to your computer and use it in GitHub Desktop.
본인이 쓰는 APIManager 구조
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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