Working with throwing functions in Swift
Tags: swift, ios
Published: 2020-02-24 08:25:09

> This post was originally published on my [blog](https://www.donnywals.com/the-blog/). You can read it in its original form [here](https://www.donnywals.com/working-with-throwing-functions-in-swift/).

In Swift 2.0, Apple introduced the `throws` keyword in Swift. This addition to Swift language added the ability for developers to write code that clearly communicates that an error might occur during the execution of a code path, and it forces the caller of that code to handle, or explicitly ignore the error in-place. In this post I will show you what the `throws` keyword is exactly, and how you can deal with errors in your codebase.

## Working with code that throws errors

If you've worked with `JSONDecoder` in Swift, you have already experienced code that can throw an error. Let's look at a quick example to refresh your mind.

```swift
do {
  let data = "hello, world".data(using: .utf8)!
  let decoder = JSONDecoder()
  let string = try decoder.decode(String.self, from: data)
} catch { error in
  print("something went wrong!")
  print(error)
}
```

This code is a very basic example of how you can work with code that might throw an error. When you call a function that can throw an error, you must prefix this invocation with the `try` keyword. You can't just do this anywhere. Calling out to code that might throw an error must occur in a `do {} catch {}` block. The `try` prefixed code must be in the `do` portion of the block. You can have more than one `try` prefixed method call in a single `do` block. When any of those method calls throws an error, execution is immediately moved to the `catch` part. 

The `catch` block receives a single argument, which is the `Error` that was thrown in your `do` block. In Swift, `Error` is a protocol that is used to represent errors. It's possible to specialize your `catch` block to make it catch only a specific kind of error. If you do this, you still need a general purpose `catch` that will catch all other `Error` types. Let's look at an example:

```swift
do {
  let data = "hello, world".data(using: .utf8)!
  let decoder = JSONDecoder()
  let string = try decoder.decode(String.self, from: data)
} catch is DecodingError {
  print("something went wrong while decoding!")
} catch { error
  print("something went wrong!")
  print(error)
}
```

You can use pattern matching to specialize catch clauses for an entire category of errors, or a specific error (`catch is MyError.someCase`). Doing this can be convenient if you have special handling paths for certain errors, but you do lose access to the original error. The final catch-all is invoked if none of the specialized `catch` blocks match the thrown error.

There are cases when you might make the decision that you don't want to handle any thrown errors. In these cases, you can use the `try?` prefix instead of `try`. When you call a throwing function with `try?`, the function's return type becomes an `Optional` that's `nil` when an error was thrown. Let's look at an example:

```swift
enum MyError: Error {
  case myErrorCase
}

func doSomething() throws {
  throw MyError.myErrorCase
}

print(try? doSomething())
```

This example is pretty worthless in a real codebase, but it illustrates my point nicely. The `doSomething()` function doesn't return anything. This means that it returns `Void`, which can also be written as an empty tuple (`()`). The printed result of this code is `nil`. If you comment out the `throw` line from `doSomething()`, the printed output is `Optiona(())`. In other words, an optional with `Void` as its value. If a function has a return value and you call it with `try?`, the result is also optional. Let's look at another example:

```swift
let data = "hello, world".data(using: .utf8)!
let decoder = JSONDecoder()
let string = try? decoder.decode(String.self, from: data)

print(string)
```

If you run this code in a Playground, you'll find that the printed value is `nil`. The provided data isn't valid JSON, so it fails to decode. But because we call `decoder.decode(_:from:)` with `try?`, the error is hidden and `decode(_:from:)` returns an optional value instead of its normal non-optional value.

If you call a throwing function that returns an optional with `try?`, you might expect a nested optional to be returned. After all, the function itself returns an optional and the return type of a throwing function is wrapped by an optional when you call it with `try?`. This means that a function that returns `String` returns `String?` when you call it with `try?`. However, if you call a function that returns `String?`, it doesn't return `String??`. Swift will automatically work around this nested optional, which is both a blessing and a curse. Consider the following code:

```swift
let string = try? returnsOptionalString()

if string == nil {
  // why is string nil? Is it due to an error? Or did the function execute successfully and we just got back nil without encountering any errors?
}
```

While the code above might be more convenient to write than a `do {} catch {}` block, you lose all error-related information. And in cases where code returns an optional, you don't whether you received `nil` because of an error. You should only use `try?` if you truly don't care about handling errors, or knowing whether an error occurred at all.

There is one more way to deal with code that can throw errors. You can use `try!` in cases where you're absolutely sure that your code shouldn't throw an error, and you want your app to crash if it does. This flavor of `try` should be used sparingly, and preferably not at all. Let's look at one last example:

```swift
enum MyError: Error {
  case myErrorCase
}

func doSomething() throws {
  throw MyError.myErrorCase
}

try! doSomething() // 
```

This code would crash at runtime. `doSomething()` always throws an error, and by calling it with the `try!` prefix we tell Swift that we don't expect `doSomething()` to actually throw an error. And when it does, execution of the program should halt, and the app should crash. This is quite radical and, again, should be used sparingly.

## Throwing errors in your own code

Sometimes, the code you write needs a way to express that something went wrong and execution of that code path needs to stop immediately with an error. If the error is recoverable, you might have a good candidate for a throwing function on your hands. When I say that an error is recoverable, I mean that the error didn't occur due to a programming error (like accessing an out of bounds index in an array for example) and that the program isn't in an unexpected or invalid state. It might simply mean that _something_ went wrong.

For example, when you try to decode invalid JSON using a `JSONDecoder`, that's not considered an error that's severe enough to crash the app. Instead, an error is thrown to let you know that something went wrong. This is an important distinction, and trying to decode invalid JSON should never crash the application. At least not in the `JSONDecoder`. You're free to crash your app is a decoding error occurs if you want but I'd strongly advise you not to. Especially if you're loading JSON from a webserver.

When you're writing your own code, you might want to throw an error of your own. You already saw how to do this in the previous section in the `doSomething` function:

```swift
func doSomething() throws {
  throw MyError.myErrorCase
}
```

Functions that can throw an error **must** have the `throws` keyword appended to their signature, before the return type. Here's what a function signature for a throwing function with a return type looks like:

```swift
func returnsOptionalString() throws -> String? {
  // do work
}
```

When you're writing code in a so-called throwing function, you can call methods that throw errors without using a `do {} catch {}` block:

```swift
func decodeJSON(_ data: Data) throws -> String {
  let decoder = JSONDecoder()
  let decodedString = try decoder.decode(String.self, from: data)

  return decodedString
}
```

This code is okay because Swift knows that `decodeJSON(_:)` might encounter and throw an error. When the JSON decoding fails in this function, the thrown error is passed up to the called of `decodeJSON` because it's marked as throwing with the `throws` keyword. When this function is called from another function that's throwing, that function will also forward the error up to its caller. The error will be passed up all the way to a caller that's not part of a throwing function. 

There is one more error throwing related keyword that I want to show you. It's called `rethrows`. The `rethrows` keyword is used for functions that don't _directly_ throw an error. Instead, the functions take a closure argument where the closure might throw instead of the function itself. Let's look at an example of a function that takes a throwing closure _without_ `rethrows`:

```swift
func execute(_ closure: (() throws -> Void)) throws {
  try closure()
}

do {
  try execute {
    print("hello!")
  }

  try execute { 
    throw MyError.myErrorCase
  }
} catch {
  print(error)
}
```

In the code above I have defined an `execute` function. This function takes a single closure argument, and all it does is execute the closure immediately. Nothing fancy. You'll see that both `execute(_:)` and the closure it receives are marked with `throws`. It's important to understand that marking a function as throwing does not mean that the function is guaranteed to throw an error. All we're saying is that it might. This is especially relevant for the closure argument. The closure that's passed might not even be capable of throwing an error, just like the first call to `execute(_:)` in this example. Even though we know that this closure never throws an error, and the compiler also knows it, we must mark the call to `execute(_:)` with `try` because that function itself might throw an error.

We can clean this code up a little bit by declaring `execute(_:)` as rethrowing rather than throwing:

```swift
func execute(_ closure: (() throws -> Void)) rethrows {
  try closure()
}

execute {
  print("hello!")
}

do {
  try execute {
    throw MyError.myErrorCase
  }
} catch {
  print(error)
}
```

Because `execute(_:)` is now rethrowing, the Swift compiler can verify whether a code path might throw an error, and it will allow you to call `execute(_:)` without `try` if it can prove that the closure you passed to `execute(_:)` doesn't throw an error. Quite neat right?

You'll find `rethrows` in several places in the Swift standard library. For example, `map(_:)` is marked with `rethrows` because the closure you supply to `map(_:)` is allowed to throw errors if needed:

```swift
let mapped: [Int] = try [1, 2, 3].map { int in
  if int > 3 {
    throw MyError.intLargerThanThree
  }

  return int * 2
}
```

This probably isn't how you commonly use `map(_:)` because typically the closure passed to this function doesn't throw. But now you know that you're allowed to throw errors while mapping, and you also know why you're not forced to mark every call to `map(_:)` with `try`. 

Note that the execution of any throwing method or closure is halted immediately when an error is thrown:

```swift
func doSomething() throws {
  throw MyError.myErrorCase

  print("This is never printed")
}
```

Throwing an error is a strong signal that something's wrong, and there's no point in fully executing the current code path. The error is sent to the caller of the throwing function, and it must be handled or forwarded from there. If you throw an error in a function that should return something, the function will not actually return anything. Instead, your code switches to the `catch` part of the `do {} catch {}` block immediately where you can handle the error. The exception here is when the called of your throwing function calls it with `try?` or `try!` like I explained in the previous section.

## In Summary

In this post, I've shown you how you can deal with functions that can throw errors in Swift. You saw how you can call functions that are marked as throwing, how you can tell Swift you're not interested in handling an error and how you can tell Swift that you're absolutely sure a certain call will never actually throw an error at runtime.

After that, I moved on to show you how you can throw errors from your own code, what the `rethrows` keyword is and when it's useful.

If you have any feedback for me about this post, or if you have any questions, don't hesitate to reach out to me on [Twitter](https://twitter.com/donnywals).