[iOS] Throwing, Try, Do-Catch

RudinP·2024년 7월 12일
0

Study

목록 보기
252/258
RunTime Error을 핸들링하는 방법을 공부한다.

Error Type

  • 보통 열거형으로 선언
  • Error 프로토콜을 채용하면 된다.
  • 일반적인 타입과 달리 스위프트의 에러 처리 시스템에 통합된다.
 enum DataParsingError: Error {
    
}

throw

  • throw expression 같이 사용된다.
  • expression에는 반드시 에러타입이 와야 한다.
  • 코드 블럭에서 에러를 던질 수 있어야 사용 가능하다.
  • throw는 에러를 던지는 키워드고, throws는 함수, 메소드, 생성자, 클로저 등 블록이 에러를 던질 수 있다 선언하는 키워드로 다르다.
//에러를 던질 수 있는 블럭
func name(parameters) throws -> ReturnType {
	statements
}

init(parameters) throws {
	statements
}

{ (parameters) throws -> ReturnType in
	statements
}

throwing function

import UIKit

enum DataParsingError: Error {
    case invalidType
    case invalidField
    case missingRequiredField(String)
}

func parsing(data: [String: Any]) throws { //파라미터 다음 throws 키워드 사용 : throwing function
    guard let _ = data["name"] else {
        //리턴처럼 현재 스코프의 실행을 즉시 끝낸다.
        throw DataParsingError.missingRequiredField("name")
    }
    
    guard let _ = data["age"] as? Int else{
        throw DataParsingError.invalidType
    }
    
    //Parsing
}
  • return과 달리 throws 키워드가 있다고 해서 항상 에러를 던져야 하는 것은 아니다.

try Expression

  • throws 함수를 호출할 때 try 표현식을 사용해야 한다.
  • 세 가지 형태가 있다.
try expression //do-catch문과 함께 사용
try? expression //Optional Try. 표현식에서 에러를 던지면 nil return
try! expression //Forced Try. 표현식에서 에러를 던지면 런타임 에러 발생

  • throws 키워드가 있는 메소드는 반드시 try 키워드를 사용해야 한다.

예시

func parsing(data: [String: Any]) throws { //파라미터 다음 throws 키워드 사용 : throwing function
    guard let _ = data["name"] else {
        //리턴처럼 현재 스코프의 실행을 즉시 끝낸다.
        throw DataParsingError.missingRequiredField("name")
    }
    
    guard let _ = data["age"] as? Int else{
        throw DataParsingError.invalidType
    }
    
    //Parsing
}

try? parsing(data: [:]) //missingRequiredField Error. nil 리턴

에러 처리 방법

do-catch

  • 에러의 종류를 확인하고 개별적으로 처리하고 싶을 때 사용
  • do블럭: 필수 블럭으로, 에러가 발생할 수 있는 코드를 여기서 호출
    • 실행 중 에러 발생 시 do 블럭 즉시 종료
    • 이어지는 catch블럭 실행
      • 특정 에러와 매칭 시켜 처리하거나, 모든 에러 처리 가능
      • where 절을 추가하여 매칭시킬 에러 패턴에 조건 추가 가능

문법

do {
	try expression
    statements
} catch pattern {
	statements
} catch pattern where condition {
	statements
}

예시

func test(){
    do{
        try parsing(data: [:])
    } catch DataParsingError.invalidType { //특정 에러 캐치
        print("handle invalidType Error")
    } catch { // 범용 에러 캐치
        print("handle error")
    }
}
// missingRequiredField Error이므로 범용 에러 캐치
// handle error 출력

  • 하나의 캐치 문에서 여러 에러 타입을 핸들링하는것도 가능하다.
  • 캐치 블록 작성 시 까다로운 패턴부터 위에 작성해야 한다.
    • if-else if처럼 위에서부터 순서대로 조건 판단

캐치문 패턴이 전부 없는 경우

  • 범용 캐치문이 없다면 컴파일 에러가 발생한다.
  • 혹은 해당 do-catch문이 있는 함수에서 에러를 다시 던져도 된다.
func test() throws {
    do{
        try parsing(data: [:])
    } catch DataParsingError.invalidType, DataParsingError.invalidField { //특정 에러 캐치
        print("handle invalidType Error")
    } 
}
  • 이렇게 되면 작성한 패턴을 제외한 에러는 test 메소드를 호출한 코드로 전달된다.
  • 이 경우 do-catch 생략 가능(test메소드를 호출한 코드에서 에러 핸들링)

패턴이 없는 캐치문만 있는 경우

  • 무슨 에러가 발생했는지 구분할 수 있도록 error라는 특별한 로컬 상수를 사용할 수 있다.
  • 특정 에러 타입이 아니므로 타입캐스팅이 필요하다.
func test() throws {
    do{
        try parsing(data: [:])
    } catch {
        print("handle erro")
        if let error = error as? DataParsingError {
            switch error {
            case .invalidType:
                print("invalid Type")
            default:
                print("handle error")
            }
        }
    }
}

try & Optional Binding

try? expression //Optional Try. 표현식에서 에러를 던지면 nil return
try! expression //Forced Try. 표현식에서 에러를 던지면 런타임 에러 발생
  • 에러의 종류와 관계없이 성공과 실패만 구분해서 처리하고 싶을 때 사용
if let _ = try? parsing(data: [:]) { //성공 시 바인딩 실행 후 success 프린트
    print("success")
} else { //실패 시 바인딩에 실패하고 else 블럭 실행. fail 프린트
    print("fail")
}
  • 반드시 옵셔널 바인딩을 해야하는 것은 아니며, 함수 호출 후 결과를 신경쓰지 않아도 된다면 그냥 옵셔널 트라이만 작성해도 된다.
try? parsing(data: [:])
profile
iOS 개발자가 되기 위한 스터디룸...

0개의 댓글