Error handler

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

func canThrowErrors() throws -> String

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

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

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

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

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

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)
}

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

let user = try? decode()

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

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

The guard Statement

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

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 ทุกครั้งที่ทำเสร็จ

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 แล้ว

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

Last updated