Enums

If you’re used to Objective-C, you might be wondering if there’s anything special about Swift enums. The answer to that is a huge YES. Swift enums are way more powerful than Objective-C enums, and I promise you that you’ll love them. And for everyone else, you’re in for a treat as well.

You can follow along the code examples by cloning this repo or downloading the Playground directly.

We’ll first go over the basics, then dive right into some examples of how you can start using enums to write safer and more predictable code. ✨

Let’s get started.

Basics

Apple’s guide The Swift Programming Language describes enums as below:

An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.

Maybe it’s because I’ve been writing a lot of UI code this week, but the first thing that comes to mind when I think of a group of related values I think of the idea of a “side” as in top, left, bottom, and right.

Turns out it’s really simple to define something like this in Swift:

You can even add raw values to your enums like Int below. The raw values are implicitly assigned as 0, 1, 2, and 3 respectively.

side-int-rawvalue

If we change Int to String, the raw values become "Top", "Left", "Bottom", and "Right" respectively.

side-string-rawvalue

At this point, you might be wondering 💭 “OK this is all cool but how can I start using this immediately?” Well I’m glad you asked! (Even if you didn’t, roll with me please. 🙏🏼)

One thing that we very often do in mobile development is talk to an API. Enums come in very handy when dealing with API’s. There are two main ways that come to my mind. The first is in API modelling and the second is in API response handling. Let’s dive in. 🐳

Enums applied to API modelling

If you are working on a messaging app, you might have a status for each message like Draft, Sent, and Failed. 📬

We can represent this as as Status enum like this:

Notice that we’ve explicitly assigned raw values instead of having them implicitly assigned. (Yes that’s another thing we can do with enums, 🆒!) You’ll see why we did this in a moment.

Next, we can use Status in our Message struct which might look something like this:

When we get a message from the API, it might come in JSON form that more or less looks something like this:

{
  "text": "Hello!",
  "status": "sent"
}

Wouldn’t it be nice if we could initialize a Message with the JSON dictionary that we get back from the API? Let’s see what this would look like.

(If guard is unfamiliar or new to you, please read Optional? which goes over everything you’ll need to know for this example.)

The most interesting line is

let status = Status(rawValue: statusString)

which is initializing a Status with a raw value. For example, Status(rawValue: "sent") would become Sent and Status(rawValue: "draft") would become Draft.

Here it is in action:

status-example

It’s such a small thing, but by using rawValue and explicitly assigning them to correspond to our API’s representations, we can write way nicer JSON deserializers and serializers without rolling our own functions that converts strings to enums and vice a versa (or worse yet, stringly-typing our code). If your API returns integers for statuses, all you need to do is change the raw value type to Int when you declare the enum, and the same pattern applies. Isn’t it so fun? 🐸

But that’s not all! I promised you two examples of using enums when dealing with API’s, so let’s keep going.

Enums applied to API response handling

When dealing with an API, there are many things that can go wrong. The request could fail because we have no network connection or the server is down, or maybe the request succeeds but we get malformed data.

Swift 2.0 changed the way that we handle error handling for most cases by introducing throws (more info on The Swift Language Guide). throws is great for guiding control flow (might write about it later if anyone is interested), but there’s one pretty big problem. We can’t use it for anything that is asynchronous, but asynchrony is the nature of API requests.

So now what? What if we wanted to hit the /messages endpoint to get a list of messages? One way we could do this is to have a completion block that provides an optional array of messages [Message]? and an optional error, like below:

The function listMessages takes in a completion handler completion that that takes [Message]? and NSError? as parameters. If the request is successful, completion gets called with messages with some [Message] result and error as nil. If it fails, completion gets called with messages as nil and error as some NSError.

This is a pretty standard approach, especially if you’re coming from Objective-C like I did. So is there anything wrong with it? Well, kind of.

What would you do if you got a result where messages is nil and error is nil? Or maybe you get non-nil messages but you also get a non-nil error. These are states that should be impossible, but they aren’t because we can write code that lets this happen. How do we write better code that doesn’t get into impossible states like these?

We prevent ourselves from writing bad code. ✋🏼

Swift enums have these things called associated values that let us write safer, more predictable code. Associated values are literally what they are called. You can associate values to your enums.

For example, we can define a Shape enum that has two cases Rectangle and Circle. Rectangles can be defined by a width and height, so we are going to associate width: Int and height: Int to Rectangle. Circles can be defined by a radius, so we are going to associate radius: Int to Circle.

Given this, we can now construct Rectangle’s and Circle’s.

square-circle

Let’s apply this to our API request example. Our problem was that we could get into weird states with messages and error both being nil or both being present. Well, if we don’t want them to be together, why not split them in to different cases? Sounds like the job for an enum! We can declare an enum called Result that has two cases: Success and Error.

With this, we can update listMessages like this:

Now we can’t get into impossible states like having both messages and an error or having neither. Time to celebrate. 🎉

But wait. What if we wanted to use this beautiful Result type for handling API responses for other endpoints like /users in addition to /messages? We can make the enum generic on type T and have the associated value’s type for the Success case. (If you are unfamiliar with generics, please take a look at Generics first!) Note that we removed the names of the associated values messages and error because they were a little redundant and also optional.

For the /users endpoint, we can define listUsers like this:

Isn’t it so clean? Don’t you just want to go off and write yourself a beautiful API client in Swift?! Well even if you’re not quite as excited to that degree, maybe you’ve learned some things that you can start applying to your code immediately. At least that’s what would make me happy!

Anyway, that’s it for now. If you have any questions, feel free to leave them in the comments below, or tweet at me @ayanonagon. The biggest reason I’m writing these posts is to get better at explaining concepts and how amazing they are, so any feedback about what was clear vs. not would be much appreciated as well. Also let me know what other topics you want to hear about. Hope you had fun learning, and thanks for reading! 🐥

More reading