# Error handler

แม้ว่าโดยทั่วไปฟังก์ชั่นใน Swift หรือจากไลบารีจะไม่ค่อยมี Error เกิดขึ้น แต่ในบางครั้ง บางฟังก์ชั่นก็มีการส่ง Error ออกมา ซึ่งเราสามารถดูได้จาก signature ของฟังก์ชั่นนั้น ฟังก์ชั่นที่สามารถส่ง Error ออกมาได้ จะมี keyword `throws` หน้าตาประมาณนี้

```swift
func canThrowErrors() throws -> String
```

เมื่อฟังก์ชั่นสามารถที่จะ throw error ออกมาได้ ฝั่งที่เรียกใช้งานฟังก์ชั่นจึงต้องเขียนรองรับการส่ง error ออกมาด้วย `do catch` โดยจะต้องมี `try` อยู่ที่หน้าฟังก์ชั่นที่ throw error ได้

```swift
do {
    let user = try decode()
    print(user.name)
} catch {
    print(error)
}
```

จากตัวอย่างโค้ด ฟังก์ชั่น decode สามารถ throw error ออกมา จึงต้องครอบด้วย `do catch` และใส่ `try` ขณะเรียก decode

ในบล๊อก `catch` จะได้ตัวแปร `error` มาโดยอัตโนมัติ

ในกรณีที่ต้องการ handle แต่ละ Error แยกกัน สามารถ catch แยกกันได้

```swift
enum VendingMachineError: Error {
    case invalidSelection
    case outOfStock
    case insufficientFunds(coinsNeeded: Int)
}

func buyFavoriteSnack() throws {
    throw VendingMachineError.insufficientFunds(coinsNeeded: 9)
}

do {
    try buyFavoriteSnack()
} catch VendingMachineError.invalidSelection  {
    print("invalid selection")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
    print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
    print(error)
}
```

ในบางครั้งเราอาจจะเห็นโค้ดแบบนี้

```swift
let user = try? decode()
```

คือไม่ใส่ `do catch` ในกรณีนี้ `user` จะกลายเป็น optional ไปด้วย และถ้าฟังก์ชั่น decode throw error ออกมา `user` จะเป็น `nil`&#x20;

ในทางกลับกัน `try!` ก็สามารถใช้งานได้เช่นกัน ถ้าไม่ error ผลลัพธ์ที่ได้จะไม่ใช่ optional แต่ถ้ามี error แอปเราก็จะ crash

**The guard Statement**

ในบางครั้งการทำ optional binding และการตรวจสอบเงื่อนไขจะทำให้เรามี `nested if` ซ้อนเข้าไปเรื่อย ทำให้เราอ่านยาก

```swift
func isValid(text: String?) -> Bool {
    if let text = text {
        if text.count != 5 {
            return false
        }
        if !text.hasPrefix("W") {
            return false
        }
        print(text)
        return true
    }
    return false
}
```

swift จึงมี `guard` ให้เราใช้ ซึ่งจะทำหน้าที่คล้าย ๆ กับตรงข้ามกับ `if` แต่ในบล๊อก guard จะต้อง return ทุกครั้งที่ทำเสร็จ

```swift
func isValid(text: String?) -> Bool {
    guard let text = text, text.count == 5, text.hasPrefix("W") else {
        return false
    }
    print(text)
    return true
}
```

จากตัวอย่าง guard จะทำ optional binding และ ตรวจสอบเงื่อนไข ถ้าทั้งหมดเป็นจริง บล๊อก guard จะไม่ทำงานและรันลงมาต่อที่บรรทัด 5 ตัวแปร text จะไม่ใช่ optional แล้ว&#x20;

แต่ถ้าไม่สามารถ unwrap text ได้ หรือเงื่อนไขไม่เป็นความจริง บล๊อก guard จะทำงานและ return false ออกไป
