스위프트에서는 런타임에 에러가 발생한 경우 에러를 처리가히 위해 에러의 발생(throwing), 감지(catching), 증식(propagating), 조작(manipulating)을 지원하는 클래스를 제공한다.
Swift에서 에러는 Error 프로토콜을 따르는 타입의 값으로 표현된다. Error 프로토콜은 비어있는데, 이 프로토콜을 따르는 타입이 에러 처리를 위해 사용될 수 있다는 것을 가르킨다.
Swift의 열거형은 특히 관련된 에러를 그룸화하고 추가적인 정보를 제공하기에 적합하다.
예를들어, 게임 자판기의 동작 에러와 관련한 에러를 다음과 같이 표현할 수 있다.
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(cinsNeeded: Int)
case outOfStock
}
에러를 위와 같이 선언하고 5개의 코인이 더 필요하다는 에러를 다음과 같이 발생시킬 수 있다.
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
에러가 발생하면 특정 코드 영역이 해당 에러를 처리하도록 해야한다. Swift에서는 4가지 방법으로 에러를 처리할 수 있다.
do-catch
문 사용assert
를 사용해 강제로 크래쉬를 발생시키는 방법어떤 메소드가 에러를 발생 시킬 수 있다는 것을 알리기 위해서 다음과 같이 throw
키워드를 함수 선언부의 파라미터 뒤에 붙일 수 있다.
func canThrowErrors() throw -> String
이러한 함수를 throwing function 이라고 하고 함수 내부에서 에러를 만들어 함수가 호출된 곳으로 전달한다.
오직 throwing function만이 에러를 발생시킬 수 있다. 만약 throwing function이 아닌 함수에서 throw가 발생한다면 반드시 그 함수내에서 throw에 대해 처리돼야 한다.
struct Item {
var price: Int
var count: Int
}
class VendingMachine {
var inventory = [
"Candy Bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
var coinsDeposited = 0
func vend(itemNamed name: String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.invalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.outOfStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
var newItem = item
newItem.count -= 1
inventory[name] = newItem
print("Dispensing \(name)")
}
}
vend(itemNamed:)
메소드의 구현에서 guard
구문을 사용해 에러가 발생하면 함수에서 에러를 발생시키고 빠르게 함수를 탈출할 수 있도록 한다. 해당 메소등는 에러를 발생시키기 때문에 이 메소드를 호출하는 메소드는 반드시 do-catch
, try?
, try!
등의 구문을 사용해 에러를 처리해야 한다.
do-catch
를 이용해 에러를 처리하는 코드 블럭을 작성할 수 있다.
do {
try expression
statements
} catch pattern 1 {
statements
} catch pattern 2 where condition {
statements
} catch {
statements
}
catch
구문 뒤에 어떤 에러인지 적고 그것을 어떻게 처리할지 명시할 수 있고 만약 catch
구문 뒤에 에러 종류를 명시하지 않으면 발생하는 모든 에러를 지역 상수인 error
로 바인딩한다.
try?
구문을 사용해 에러를 옵셔널 값으로 변환할 수 있고, 에러가 발생한다면 nil이 된다.
try?
는 만약 발생하는 모든 에러를 같은 방법으로 처리하고 싶을 때 사용한다. 예를 들어, 다음 코드는 데이터를 가져오는 여러 접근 방법을 시도하는데 접근 방법이 모두 실패하면 nil을 반환한다.
func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}
메소드에서 에러가 발생되지 않을 것이라고 확신하는 경우 try!
를 사용할 수 있다.