Throwable Optionals

      No Comments on Throwable Optionals

You might be wondering what are throwable optionals. It's simply an optional that will throw when force unwrapping. Throwable optionals don't exist in the swift standard library. In this blog article we'll create them and we'll create a custom operator for them as well.You might be wondering what are throwable optionals. It’s simply an optional that will throw when force unwrapping. Throwable optionals don’t exist in the swift standard library. In this blog article we’ll create them and we’ll create a custom operator for them as well. This article has been inspired by a great article by Erica Sadun.

Problem Domain

This article addresses a very specific problem. You might have the same problem, or a completely different one. In each case, you might find inspiration on how to solve it here. Let me begin by explaining what was the problem we were facing.

One morning our build engineer told us that one of the build servers is stalling, the nightly build  kicked off, and it was still running. A process that usually takes less than 20 minutes was taking hours to complete. He moved on to investigate and found that Xcode crashed while running unit tests. As soon as we looked into the logs, we knew immediately what went wrong. The tests were force unwrapping an optional value that was ‘nil’.

I really don’t like force unwrapping, and I tend to avoid it at all costs. Sometimes it makes sense to use it, but most times there’s a better way. Why our tests were using force unwrapping is not the topic here 🙂 We could have fixed the problem in a number of ways (guard statements, nil checks, if-let…) but we wanted to find an elegant solution to our problem.

We stumbled upon a article by Erica Sadun where she created a custom function that was throwing errors if the optional was nil. This was perfect for us. Now our tests were throwing, and if the unwrap operation throwed an error, the test would fail automatically. Xcode wouldn’t crash, the pipeline would flow, the build engineer would be happy… 🙂

This was our story. Maybe you have the same problem, maybe not. But for the sake of knowledge sharing and academic curiosity, let me share some code with you.

The Code

If you’ve read one of the previous articles on the blog, you know already that optionals are simply enums. You can read more about them here: Optionals in Swift, Understanding Optionals. What we’ll do is simply create an extension on Optional enum that will have a custom function. So every optional in the app will have access to it. We’ll need a custom error, so let’s do that first:

public enum ThrowableOptionalError: Error {
    case unableToUnwrap
}

We’ll be throwing this error if the optional is nil. Next up is the actual function:

extension Optional {
    public func unwrap() throws -> Wrapped {
        switch self {
        case .some(let value):
            return value
        default:
            throw ThrowableOptionalError.unableToUnwrap
        }
    }
}

And that’s it. Let’s add a bit of syntactic sugar, while we’re at it:

static postfix func +!(value: Optional<Wrapped>) throws -> Wrapped {
    return try value.unwrap()
}

Here we defined a custom postfix operator, so we can use ‘+!’ instead of calling the unwrap function.

Using It

This will be a short paragraph 🙂 I’ll be using it in tests, so I can give you an example of how to use it there. Bear in mind, this is a simple, fictive example… So don’t judge me too harshly 🙂

func testThrowableOptional() throws {
    var nilledValue: Int! = 42
    nilledValue = nil
        
    XCTAssertEqual(try nilledValue+!.description, 42.description)
}

We have to mark the test method as throwing, and we have to use the ‘try’ keyword when using our custom operator, but that’s pretty much it. If we were to run this test it would fail:

In contrast, if we tried to force unwrap a value without using our brand new throwable optional, the test would crash, like so:

That’s pretty much it. A small custom operator that might save you some headache.

Conclusion

I don’t really like force unwrapping. It’s asking for trouble. But, sometimes it makes sense to crash your app if a value is not present, but that doesn’t mean you should crash your tests as well. This article covered that scenario. How to crash your app, but fail the tests.

This was a short little article that I wanted to share with you guys. It was very specific but hopefully you’ll find some inspiration from it. It certainly helped us solve our problems. I really hope you learned something new today. You can find the example project on GitHub.

As usual, have a nice day 🙂

Dejan.

More resources

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.