Swift Tutorial

Swift Data Types

Swift Control Flow

Swift Functions

Swift Collections

Swift OOPs

Swift Additional Topics

Swift - Error Handling

In Swift, error handling is done using a distinct mechanism involving the throw, try, catch, and throws keywords. It offers a clear and powerful way to handle runtime errors. Let's go through each component:

1. Defining Error Types:

First, you typically define possible errors using an enum that conforms to the Error protocol:

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

2. Throwing Errors:

You can throw errors from functions or methods by marking them with the throws keyword. Within these functions or methods, you can use the throw keyword to indicate an error has occurred.

func vend(itemNamed name: String) throws {
    // Some logic here
    if /* some condition */ {
        throw VendingMachineError.outOfStock
    }
    // Some more logic
}

3. Handling Errors:

There are several ways you can handle errors in Swift:

a) Using Do-Catch:

This is the primary way to handle errors. You execute the code that might cause an error within a do block and handle errors in the corresponding catch blocks.

do {
    try vend(itemNamed: "Candy Bar")
    // Code executed if no error is thrown
} catch VendingMachineError.outOfStock {
    print("Out of stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
    print("Insufficient funds. Please insert \(coinsNeeded) more coins.")
} catch {
    print("An unexpected error occurred: \(error).")
}

b) Using Optional try?:

If you use try? while calling a throwing function, the result will be an optional. If the function throws an error, the result will be nil.

if let result = try? someThrowingFunction() {
    // Use the non-nil result
} else {
    // Handle the error or the fact that result is nil
}

c) Using Forced try!:

If you're sure that a throwing function won't actually throw, you can use try!. However, if an error does get thrown at runtime, your app will crash.

let result = try! someThrowingFunctionThatWillNeverThrow()

4. Propagating Errors:

You can mark a function with throws to indicate that it can throw an error, but instead of handling the error inside, it passes it up to the calling function to handle.

func purchaseItem(named name: String) throws {
    // Some code
    try vend(itemNamed: name)
    // Some more code
}

5. Cleanup Actions:

Swift provides the defer keyword to execute a set of statements just before code execution leaves the current block of code, ensuring that cleanup actions are always executed.

func someFunction() {
    // some setup code
    defer {
        // cleanup code that runs at the end, even if an error is thrown
    }
    // main code
}

In Summary:

Swift's error-handling mechanism is designed to help developers write safe and predictable code by clearly distinguishing between normal code flow and error handling. It ensures that errors are propagated and handled in a clear and expressive manner.

  1. How to handle errors in Swift programming:

    • Description: Error handling in Swift allows you to gracefully respond to and manage errors that might occur during the execution of your code.

    • Code:

      enum CustomError: Error {
          case someError
      }
      
      func doSomething() throws {
          // Code that might throw an error
          throw CustomError.someError
      }
      
      do {
          try doSomething()
      } catch {
          print("An error occurred: \(error)")
      }
      
  2. Swift do-catch statement for error handling:

    • Description: The do-catch statement is used to handle errors in Swift. Code that can throw errors is placed inside the do block, and error-handling code is in the catch block.

    • Code:

      do {
          try doSomething()
      } catch let error as CustomError {
          print("Custom error occurred: \(error)")
      } catch {
          print("An error occurred: \(error)")
      }
      
  3. Custom error types in Swift:

    • Description: You can define custom error types by conforming to the Error protocol.

    • Code:

      enum CustomError: Error {
          case someError
      }
      
      func doSomething() throws {
          throw CustomError.someError
      }
      
  4. Error propagation in Swift functions:

    • Description: Functions can propagate errors using the throws keyword, indicating that the function may throw an error.

    • Code:

      func performTask() throws {
          // Code that might throw an error
      }
      
      func callingFunction() {
          do {
              try performTask()
          } catch {
              print("An error occurred: \(error)")
          }
      }
      
  5. Swift throwing functions and non-throwing functions:

    • Description: Functions that may throw errors are marked with throws, while non-throwing functions do not have this keyword.

    • Code:

      func throwingFunction() throws {
          // Code that might throw an error
      }
      
      func nonThrowingFunction() {
          // Code that does not throw an error
      }
      
  6. Handling specific errors in Swift:

    • Description: You can use multiple catch blocks to handle different types of errors, allowing for more specific error handling.

    • Code:

      do {
          try doSomething()
      } catch CustomError.someError {
          print("Specific error occurred")
      } catch {
          print("An error occurred: \(error)")
      }
      
  7. Chaining errors in Swift:

    • Description: Errors can be chained by throwing other errors within the catch block, allowing for more complex error handling scenarios.

    • Code:

      func performTask() throws {
          throw CustomError.someError
      }
      
      do {
          try performTask()
      } catch CustomError.someError {
          throw AnotherError.anotherError
      } catch {
          print("An error occurred: \(error)")
      }