#108: Error Handling 📛


Today we're going to talk about Error Handling. First though, a bit of a pitch: Great apps handle errors gracefully. Think of error handling as the dental flossing of creating apps. Sure, not the most exciting part of the job, but very important. Alright, let's dive in:

enum BlueprintFileError: ErrorType {
  case Interrupted
  case CorruptData
  case ShipBoardedByVader

func decryptDeathStarPlans() throws -> BlueprintFile {
  // ...

  throw BlueprintFileError.Interrupted

To allow one of our functions to throw an error, we add a throws keyword to its definition. Then we can create our own enum that inherits from the system's ErrorType.

Then anywhere inside our function, when something goes wrong, we can throw the appropriate error.

Elsewhere in our code, we can use a do/catch block to handle these errors. We'll try our dangerous code in the do block, then catch any errors below.

do {
  try decryptDeathStarPlans()
} catch BlueprintFileError.Interrupted {
  alert("Decryption Interrupted!", options: [ "Try Again", "Cancel" ])
} catch BlueprintFileError.CorruptData {
  alert("Sorry, the file could not be read from disk.", options: [ "OK" ])
} catch BlueprintFileError.ShipBoardedByVader {
  alert("Ship is being boarded, " +
    "decryption will continue on R2-D2", options: [ "OK" ])
} catch let error {
  alert("Decription Failed", options: [ "Try Again", "Cancel" ])

Finally, let's look at how best to actually handle these errors. Every app is unique, and will need special consideration around how best to handle its errors. That being said, here's some guidelines that should apply in most cases, and are illustrated above:

  • Fail as gracefully as possible, and preserve as much of the user's work as possible.
  • If necessary, tell the user what happened in clear simple terms. (No jargon).
  • When possible, give the user a way to try the task again.
  • Handle all cases, even if unlikely, if it can go wrong, it will, for someone.