Magical Swift Enums — Going Past Basic

In my last article, we covered a good introduction on Swift enums. Please refer to that one for a quick intro on enums. For this article, I want to show you a couple of powerful features that make enumerations really special. Today’s topic will include extending an enumeration with protocols and adding methods. We will also revisit associated values briefly. If all goes well, we are going to build an enumeration that can be compared for equality, store values with each type, and can print itself. Ok, let’s get cranking!

Recap of Enums

As a short recap, Apple defines an enumeration to be a grouping of related values in code. This part is the same as all other higher level programming languages. But Apple didn’t stop there in enum creation. Instead, Apple super charged the enumeration implementation by making enums first-class citizens in the Swift language with the powers to adopt features that you would find with structs, classes or built-in types. That means that you can can extend an enumeration with a Protocol or add methods/functions to the enum. You can also add associated types to store additional values with a concrete enum type.

“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.”

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2.1).” iBooks.

Equality and extending Enums

Comparing types is a pretty basic concept that is needed in all languages. We have all come across the need to compare two values with == and !=. In Swift, a type needs to adopt the Equatable protocol to be able to use == or != for comparison. So, to get this functionality, you have to subclass NSObject (or another type that implements the Equatable protocol) or create a type that adopts the Equatable protocol yourself.

The Equatable protocol defines a single operator function (== operator). The != operator is defined for you. As an aside, operator functions need to have global scope and are therefore defined outside of the enumeration definition in our case.

We also need a quick overview on Switch statements to make our comparison code concise and compact. Switch statements allow us to use to important things that will make our comparison job very easy. You can build switch statements with tuples to test multiple values at once and can use value binding to create variables that are accessible inside a switch case’s body. Let’s look at an example to make this concrete.

“Each element of the tuple can be tested against a different value or interval of values.”

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2.1).” iBooks.

So, let’s break this down. First we define an enum called RepeatFrequencyEnum that adopts the Equatable protocol. In the enum definition, we also added associated values for each type except for the NoRepeat type. For example, we can associate an Int value and an array of Months values for the Yearly type. Next, we define the global ==operator for the enum. Notice that the operator function defines two variables of RepeatFrequencyEnum type and returns a Bool value. We use an exhaustive switch on a tuple value of two RepeatFrequencyEnum’s. Using a combination of value binding and where clauses, we can give our tuple values names and use them to test for equality. I would argue that we just simplified a huge task of if/else conditionals into a few case statements. Stylistically, I chose to use a switch statement for my comparison logic. If we need to add an additional type to our RepeatFrequencyEnum, we would only need to add the type to the enum and a new case statement to the ==operator function.

“A switch case can bind the value or values it matches to temporary constants or variables, for use in the body of the case. This is known as value binding, because the values are “bound” to temporary constants or variables within the case’s body.”

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2.1).” iBooks.

Extending Enums with Methods

This next idea isn’t necessarily intuitive. Why would I want to add a method to an enum? I’ve thought about this one a bit and would propose there are actually lots of reasons to do so. If you think about the utility classes we all tend to code, maybe it might be clearer on how this feature could be useful. Think about the times that you may have written a routine to initialize or create a type. Maybe you created a factory method to return a type based on state parameters passed in. One implementation could be to create the routine in a utility class that has other related factory methods or utility functions to act on types. However, the drawback is that you potentially have moved your code too far from your types and have introduced unwanted dependencies on or have exposed details about models types that should have remained private to only the API designer. Of course I’m only proposing one example case where it might be beneficial to keep the methods closer to implementation of the enum.

Again, trying to rely on concrete examples, let’s revisit our RepeatFrequencyEnum enum and see how we might add more functionality to it. Suppose that we wanted to determine if a given date meets the criteria for the stored enum. In other words, we want to know, based on a start date, what the next repeat day is using stored criteria. How could we accomplish that? We could create a function that takes a date and give you back the next repeat date based on the enum type. See below for how you might create an implementation.

In my implementation, I created a method that has knowledge of it’s state and can use that information to create a new date. What’s cool is that the method is closely tied to the enumeration and extends it’s value. The implementation is defined in the enum, so I don’t have to worry about searching for another location to find it.

Creating Self-Printing Enums

The last topic I want to cover is how to print enums with custom descriptions. This is extremely handy when logging or debugging and is way more intuitive than seeing an address location in a debugging session. To change the text that your enum, struct, or class prints when the description function gets called, you just need to implement the CustomStringConvertible protocol. The CustomStringConvertible protocol has a single property that you need to implement.

In our case, I have extended our RepeatFrequencyEnum to add the protocol in a separate file. That way, I can include the extension when I want to but can exclude it from being available to other developers just as easily. By defining the description property and a switch statement, I can print a customize statement for each enum type and include the associate value(s) for the type as well.

Well, that’s all for today. The goal is to whet your appetite, point out some of the language possibilities, and to keep the posts short so you will actually read them! Enjoy.