천원의 개발

iOS Result 타입을 활용한 에러처리 본문

iOS&Swift🍎/iOS

iOS Result 타입을 활용한 에러처리

천 원 2022. 11. 1. 05:45

Result ?

Swift 5.0 부터 지원하는 에러를 처리하는 방법입니다.

 

Result 타입 정의

enum Result<Success, Failure: Error> {
    case success(Success)
    case failure(Failure)
}

 

 

enum을 활용하여 success와 failure의 case로 구분 지어주고 제네릭을 타입을 연관값에 넣어주어 사용합니다.

어떻게 사용할지 감이 안 잡히시죠 한번 같이 사용해 봅시다.

 

 

 

Error프로토콜을 채택한 APIError

enum APIError: String, Error {
    case invalidRessponse = "응답이 없습니다."
    case noData = "데이터가 없습니다."
    case failedRequest = "요청에 실패 하였습니다."
    case invalidData = "검색어와 일치하는 레시피가 없습니다."
    case failedResponse = "응답을 받을 수 없습니다."
}

 

아래는 간단한 API 통신을 처리하는 코드 입니다. 여기서 보면 completion이라는 클로저를 활용하여 error가 있을때는 CookRecipe는 nil값을 전달하고 APIError는 .failedRequest를 전달하는 모습을 확인 할 수있습니다. 이렇게 코드를 구성하게 되면 우리가 원하는

 

(CookRecipe, APIError)

nil, true

true, nil 

두개의 상황만 처리 해주면 되지만 

nil, nil

true, true

상황 까지 처리 하는 코드가 되어 버리는 것이죠

 func requsetAPI(text: String, completion: @escaping (CookRecipe?, APIError?) -> Void {
   URLSession.shared.dataTask(with: url) { data, response, error in
        DispatchQueue.main.async {
            guard error == nil else {
                completion(nil, .failedRequest)
                return
            }

            guard let data = data else {
                completion(nil, .noData)
                return
            }

            guard response.statusCode == 200 else {
            	completion(nil, .failedResponse)
                return
            }

            do  {
                let result = try JSONDecoder().decode(CookRecipe.self, from: data)
                completion(result, nil)
            } catch {
            	completion(nil, .invalidData)
            }
        }
        }.resume()
 
 }

 

이번에는 Result타입을 활용한 코드를 확인 해보겠습니다.

클로저의 매개변수 타입을 Result<CookRecipe, APIError>로 변경 해줍니다.

func requsetAPI(completion: @escaping (Result<CookRecipe, APIError>) -> Void) {

    URLSession.shared.dataTask(with: url) { data, response, error in
        DispatchQueue.main.async {
            guard error == nil else {
                completion(.failure(.failedRequest))
                return
            }

            guard let data = data else {
                completion(.failure(.noData))
                return
            }

            guard response.statusCode == 200 else {
                completion(.failure(.failedResponse))
                return
            }

            do  {
                let result = try JSONDecoder().decode(CookRecipe.self, from: data)
                completion(.success(result))
            } catch {
                completion(.failure(.invalidData))
            }
        }
    }.resume()
   }

실패시에는 case .failure와 연관 값으로 APIError의 case를 넣어주고 성공시에는 case .success와 CookRecipe 데이터를 넣어주는 방식으로 동작합니다.

확실히 받아올 때 옵셔널 처리를 안해줘두 되고 enum을 통한 failure와 success가 명시적으로 보이니 명확히 구분 할 수 있는것 같습니다.

 

 

함수 호출하는 코드

requsetAPI { result in
    switch result {
    case let .success(data):
        print(data)
    case let .failure(error):
        switch error {
        case .invalidData, .failedResponse, .invalidRessponse, .noData, .failedRequest:
          	print(error.rawValue)
        }

    }
}