맛동산이

Swift) Return type과 에러처리 본문

앱/Swift

Swift) Return type과 에러처리

진ddang 2023. 7. 4. 13:35

기존의 에러 핸들링(completionHandler)

기존의 escaping closure의 completionhandler를 통해서 비동기처리를 해줄때, error처리가 상당히 어려워진다.

예시로써

func fetchData(completion: @escaping (Data?, Error?) -> Void) {
    //...
}

fetchData { (data, error) in
    guard error == nil else { handleError(error!) }
    
    guard let data = data else { return }
    
    completion(data)
}

이러한 코드에서 실제로 예외처리가 가능한 경우의 수는 다음과 같다.

  1. Data, Error : True, False
  1. Data, Error : True, True
  1. Data, Error : False, True
  1. Data, Error : False, False

즉 실제로 우리가 처리하려고 하는 1번과 3번보다 더 애매한 혹은 이상한 에러가 발생할수 있다는 문제가 존재

이를 swift에서는 result type을 통해서 깔끔하게 해결할수 있다.

Result type

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

result란 이넘타입이며, where절을 이용해서 failure에서는 error가 들어와야한다고 정의되어 있다.

따라서 위의 코드를 다음과 같이 정리가 가능하다.

func fetchData(completion: @escaping (Result<Data>) -> Void) {
    //...
}

fetchData { result in
    switch result {
    case .success(let data):
        // load된 data 처리
    case .failure(let error):
        // error 처리
    }
}

예외처리(Throws, Throw)를 통한 에러처리

  • 예외처리가 필요한 이유는 optional타입은 오류가 발생했을 때 오류에 대한 정보를 외부로 전달할 방법이 없기 때문이다.

에러타입

Error타입은 모든 에러타입을 포함하기 때문에 정확하게 어떠한 에러가 발생했는지에 대해서 파악하기 어렵다.

이를 associated error를 정의하여 사용하면 좀더 좋은 에러핸들링이 가능하다.

예시는 다음과 같다.

에러타입 정의

enum testError: Error {
    case overSizeError
    case incorrectData(data: Int)
}

오류를 처리해줌

  • throws는 해당 함수가 오류를 반환할수 있다는 의미
  • throw는 return과 동일하게 에러를 반환한다는 의미이다.
// 오류가 나는 조건을 throws와 함께 배치
func throwError(someValue: Int) throws -> Int {
    guard someValue <= 10 else {
        throw DataError.overError
    }
    guard someValue >= 0 else {
        throw DataError.incorrectData(data: someValue)
    }
    return someValue
}

do - try - catch

func getNextYear(inputNumber: Int) -> Int {
    var number: Int = 0
    do {
        year = try throwError(someValue: number)
    } catch DataError.overError {
        print("값이 초과함")
    } catch DataError.incorrectData(let part){
        print("값이 정확하지 않음")
    } catch {
        print("default error catch")
    }
    
    return number
}

do try catch를 통해서 에러를 핸들링할수잇음

  1. do를 통해서 먼저 try를 함.
  1. try의 결과가 정상적이면 return number
  1. try에서 에러가 발생했는데 DataError.overError면 해당 클로저
  1. incorrectData면 해당 클로저로 감

try?, try!

이전에도 작성했지만,

try?

  • 에러 발생 시 nil 반환합니다.
  • 에러가 발생하지 않으면 반환 타입은 옵셔널(Optional)입니다.
  • 반환 타입이 없어도 사용 가능합니다.
  • do-catch문 없이 사용가능합니다.

try!

  • 에러 발생시 crash
  • 반환타입은 언래핑됨

Result와 associated error를 사용한 코드

1. 에러타입 정의

enum loadingError {
				case .networkUnavailable
        case .timedOut
        case .invalidStatusCode(let code)
}

2. 에러를 result에 삽입

typealias Handler = (Result<Data, LoadingError>) -> Void
  • 이렇게 typealias를 이용하면 코드가 좀더 보기 편하다.

3. 에러 핸들링

func fetchData(completion: @escaping Handler) { result in 
	switch result {
		.success(let data):
				self.completion(data)
		.failure(let error):
			switch error {
				case .networkUnavailable:
            self?.showErrorView(withMessage: .unavailable)
        case .timedOut:
            self?.showErrorView(withMessage: .timedOut)
        case .invalidStatusCode(let code):
            self?.showErrorView(withMessage: .statusCode(code))
	}
}


참고

[Swift] Result 타입
개 요 작업(Task) 중에는 실패할 수 있는 작업이 있습니다. 디스크에 파일을 쓰거나, API를 호출해 네트워크를 통해 데이터를 가져온다거나, 특정 URL에 있는 데이터를 불러오는 작업이 이 경우에 해당합니다. 이런 실패 가능한 작업을 처리하기 위해 Swift에서는(4.2 이하 버전) do,try, catch,throws 문법을 제공했습니다.
https://jusung.github.io/Result-타입/
[swift] 12. 예외 처리 (throws, throw, do - try - catch)
1. 예외 처리 필요한 이유 - optional타입은 오류가 발생했을 때 오류에 대한 정보를 외부로 전달할 방법이 없음 2. 구현 방법 - 함수가 반환할 오류는 일관된 주제와 연관된 경우(문자열이 있을 때, 문자열 크기, 문자열 형식, 문자열 포멧형식 등의 체크) - 일관된 주제를 표현하기에 가장 좋은 것은 enum형 타입 1) enum에 오류타입명 정의 // enum형으로 error타입명 정의 ( 프로토콜을 구현한 것은 오류 타입으로 사용하라는 일종의 가독성 표시 ) enum DateParseError : Error { case overSizeString case incorrectData(part: String) } 2) 오류가 나는 조건을 명시 : throws와 throw로 선언 - 오류가 발생할 수..
https://ios-development.tistory.com/16


Uploaded by N2T

반응형