When enums have superpowers (Swift)

Thomas Sattlecker
Making Mimo
Published in
4 min readMar 13, 2018

At Mimo, we have a weekly Show & Tell meeting where every team presents what they have worked on last week, and afterward, there is room to share something that you’ve recently learned or that you are passionate about. I created the following post after talking about “When enums have superpowers” at Mimo because I guess this could be interesting for more developers. This is the Swift version. You can also find a Kotlin version of this post here.

Let’s assume we have an iOS app written in Swift and we now want to add a user login. We create a simple login function with 4 parameters. username , password , success callback , failure callback.

So far so good. Let’s also assume that we already planned ahead. For the case that we run into an error while logging-in we created a class IncorrectPasswordError to handle this specific error in our failure callback.

Our solution works. We can add more specific errors like TimeoutError or UsernameNotFoundError and react to them in our failure callback. So why would we even want to change that if it does work? 🤔

Problems with this solution

  • We rely on type casting to handle specific errors.
  • We mix together all AuthenticationErrors with any other RuntimeError that might occur
  • The different errors are hard to discover.

While type casting might be less of a problem if you know what you are doing, the other two points still stand. During our login, we have a few errors that are basically part of the login flow like a user typing in an incorrect password/email or when a timeout occurs. These kinds of errors will be treated differently than any other unexpected error that might happen, so maybe they should be handled separately.

The second big problem is discoverability. Without looking at the exact implementations we have no idea what kind of errors we need to deal with. The compiler also will not help us here. So how can we do better?

Using Enums

Swift has a powerful feature (Enums with Associated Values) where enums can hold bigger chunks of data. We will use this to refactor our login flow.

We start by modeling out all the cases that can happen and that we want to handle specifically.

  • success — logging in successfully will contain the user object.
  • usernameNotFound — username not found
  • incorrectPassword — incorrect password
  • timeout
  • unexpectedError — any other unexpected error. These errors are now separated from the rest.

Our new login function now only has a single callback with a LoginResult. To handle the different results we create a simple switch case.

What improved

First of all we now have separated our login flow errors from any other unexpected error. These unexpected errors are handled together in a single case block while others like usernameNotFound or incorrectPassword have their own case block. The second thing we improved is discoverability. Looking at the LoginResult enum we can see exactly what might happen and what we should handle. We don’t need to know the internal implementation of our login function anymore. Another cool aspect of this solution is passing around data which is only important for the specific enum case. Only on a .success do we get a user object in the result. It is also easy to add another case.

Advantages of Enums

The biggest upside of using enums is a clear API and discoverability. Any developer using our login function will know immediately what to expect. What errors are a specific part of the login flow and need to be handled. There are lots of cases where we can make our APIs clearer and easier to use. Enums with advanced values can help a lot. So give them a try.

--

--

Thomas Sattlecker
Making Mimo

Teaching how to code @getmimo | ☕️ love good espresso ☕️