Swift Error Handling
Managing and responding to error conditions in your code
โ ๏ธ What is Error Handling?
Error handling allows you to respond to and recover from error conditions in your program. Swift provides first-class support for throwing, catching, propagating, and manipulating recoverable errors at runtime.
enum ValidationError: Error {
case tooShort
case tooLong
case invalidCharacters
}
func validatePassword(_ password: String) throws -> Bool {
if password.count < 6 {
throw ValidationError.tooShort
}
if password.count > 20 {
throw ValidationError.tooLong
}
return true
}
do {
try validatePassword("abc")
} catch ValidationError.tooShort {
print("Password is too short!")
} catch {
print("Other error: \(error)")
}
Output:
Password is too short!
Error Handling Components
Throw
Functions can throw errors using throw keyword
func riskyFunction() throws {
throw MyError.failed
}
Try
Call throwing functions with try keyword
do {
try riskyFunction()
}
Catch
Handle specific errors with catch blocks
catch MyError.failed {
print("Function failed")
}
Error Protocol
Define custom errors conforming to Error
enum MyError: Error {
case failed
}
๐น Basic Error Handling
Here's how to define and handle errors:
// Define custom errors
enum MathError: Error {
case divisionByZero
case negativeNumber
}
// Function that can throw errors
func divide(_ a: Double, by b: Double) throws -> Double {
if b == 0 {
throw MathError.divisionByZero
}
return a / b
}
func squareRoot(_ number: Double) throws -> Double {
if number < 0 {
throw MathError.negativeNumber
}
return number.squareRoot()
}
// Using do-catch to handle errors
do {
let result1 = try divide(10, by: 2)
print("10 รท 2 = \(result1)")
let result2 = try divide(10, by: 0) // This will throw
print("This won't print")
} catch MathError.divisionByZero {
print("Error: Cannot divide by zero!")
} catch MathError.negativeNumber {
print("Error: Cannot calculate square root of negative number!")
}
Output:
10 รท 2 = 5.0
Error: Cannot divide by zero!
๐น Different Ways to Handle Errors
Swift provides several ways to handle throwing functions:
// Method 1: try? - Convert to optional
let result1 = try? divide(10, by: 2) // Optional(5.0)
let result2 = try? divide(10, by: 0) // nil
if let safeResult = result1 {
print("Safe result: \(safeResult)")
}
// Method 2: try! - Force unwrap (use carefully)
let result3 = try! divide(10, by: 2) // 5.0 (crashes if error)
print("Forced result: \(result3)")
// Method 3: Nil coalescing with try?
let result4 = (try? divide(10, by: 0)) ?? 0
print("Result with default: \(result4)")
// Method 4: Multiple catch blocks
do {
let value = try squareRoot(-4)
print("Square root: \(value)")
} catch MathError.negativeNumber {
print("Cannot calculate square root of negative number")
} catch {
print("Unexpected error: \(error)")
}
Output:
Safe result: 5.0
Forced result: 5.0
Result with default: 0.0
Cannot calculate square root of negative number