- Throw various errors when parsing response
class ModelUtil {
// 1. 응답이 JSONObject나 JSONArray가 아님
// 2. 응답의 전체를 감싸는 key값으로 parsing이 안됨
// 3. key값인 Array 각각이 JSONObject가 아님
@discardableResult
static func apiFormatError() -> NSError{
let error = NSError(domain:"com.companyname.appname.API JSON Format Error", code:0, userInfo:nil)
Crashlytics.sharedInstance().recordError(error)
return error
}
@discardableResult
static func apiFormatError(userInfo: Dictionary<String, Any>) -> NSError{
let error = NSError(domain:"com.companyname.appname.API JSON Format Error", code:0, userInfo:userInfo)
Crashlytics.sharedInstance().recordError(error)
return error
}
// 필수로 받아와야 하는 값이 parsing되지 않음
@discardableResult static func apiEssentialValueParsingError() -> NSError {
let error = NSError(domain:"com.companyname.appname.API JSON Essential Value Parsing Error", code:0, userInfo:nil)
Crashlytics.sharedInstance().recordError(error)
return error
}
// 1. parsing된 값이 empty임
// 2. parsing된 값이 상황상 null이면 안 되는데 null임
static func apiEmptyValueError() {
let error = NSError(domain:"com.companyname.appname.API JSON Empty Parsed Value Error", code:0, userInfo:nil)
Crashlytics.sharedInstance().recordError(error)
}
// 요구사항에서 받기로 했던 length와 API 결과 length가 다를 때
static func apiLengthError() {
let error = NSError(domain:"com.companyname.appname.API JSON Length is Not Expected", code:0, userInfo:nil)
Crashlytics.sharedInstance().recordError(error)
}
}
enum ModelError: Error {
case invalidFormat(data: [String: Any])
}
Model class
extension IngredientPack {
static func getIngredientPack(_ requestManager: RequestManager, type: PackType, completion: @escaping (RequestResult<IngredientPack>) -> Void) {
let url = CustomRequest.domainAppend("APIPath")!
requestManager.request(url: url, parameters: ["type": type.rawValue]) { (result) in
switch result {
case .success(let data):
do {
let ingredientPacks = try JSONDecoder().decode([IngredientPack].self, from: data)
guard let ingredientPack = ingredientPacks.first else {
throw ModelUtil.apiFormatError()
}
completion(.success(ingredientPack))
} catch {
completion(.failure(.others))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
2. Throw errors when the status code is not normal
func parseResponse(request: URLRequest, _ error: Error?, _ response: URLResponse?, _ data: Data?, completion: @escaping (RequestResult<Data>) -> Void) {
// Network Indicator Control.
self.networkIndicatorCount -= 1
// Handling Error.
if let error = error {
ResponseError.cocoaError(request: request, error: error)
completion(.failure(.others))
// Handling Response Data.
} else {
if let httpURLResponse = response as? HTTPURLResponse {
if httpURLResponse.statusCode != 403
&& (httpURLResponse.statusCode < 200
|| httpURLResponse.statusCode >= 400){
let statusCodeError = ResponseError.statusCodeError(statusCode: httpURLResponse.statusCode, request: request)
Crashlytics.sharedInstance().recordError(statusCodeError)
completion(.failure(.others))
return
}
}
// (1) Data 형식이 JSON형태가 아닐 경우 Error 처리.
// (2) Data 형식이 JSON형태이고 값이 UserAuthError인 경우 Error 처리.
// (3) Data 형식이 JSON형태이고 값이 UserAuthError가 아니면 success처리.
if let data = data, let response = response {
if (data.count > 0) {
// TODO: UserAuthError를 확인하는 방법으로 Result접근이 아니라 StatusCode로 분별하기.
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
// 인증 에러 인 경우 JSON형식으로 UserAuthError라는 Key에 Message값을 보냄.
if let json = jsonObject as? [String: Any],
let authErrorMessage = json["UserAuthError"] as? String {
ResponseError.userAuthError(request: request, response: response, json: json, authErrorMessage: authErrorMessage)
completion(.failure(.authenticationFailed(message: authErrorMessage)))
return // .success가 호출되지 않도록 return
}
} catch {
let nsError = error as NSError
var userInfo = nsError.userInfo
let responseData = String(data: data, encoding: .utf8).or("nil")
userInfo.updateValue(responseData, forKey: "responseData")
let error = NSError(domain: nsError.domain, code: nsError.code, request: request, userInfo: userInfo)
Crashlytics.sharedInstance().recordError(error)
completion(.failure(.others))
return // .success가 호출되지 않도록 return
}
}
completion(.success(data))
// Handling No Data.
} else {
ResponseError.noData(request: request)
completion(.failure(.others))
// TODO: CustomRequest.
// 정상적인 상황에서도 response가 없는 요청도 있기 때문에 completeHandler가 있는 요청에 대해서만 (response를 바라는데 error 가 발생 한 경우) 에러를 리포팅 한다.
// Response가 없는 요청 알아둘 것.
}
}
}
3. Third party login error
@objc func login(on viewController: UIViewController) {
if let currentToken = AccessToken.current {
if let tracker = loginEventTracker {
let loginResult = LoginManagerLoginResult(token: currentToken, isCancelled: false, grantedPermissions: [], declinedPermissions: [])
tracker(loginResult, nil)
} else {
fetchUser(from: viewController)
}
} else {
loginManager.logIn(permissions: MyFacebook.permissions, from: viewController) { (result, error) in
if let error = error {
Alert.showConfirmAlert(withTitle: self.errorTitle, message: error.localizedDescription, viewController: viewController)
Crashlytics.sharedInstance().recordError(error)
self.thirdPartyLoginDelegate?.logFail(registerType: MyFacebook.registerType, message: self.errorMessage)
} else if result?.isCancelled == false {
self.fetchUser(from: viewController)
}
}
}
}