Using associated values effectively

Good programming is about more than just grand, universal concepts of how to write effective code. The best programmers know how to play to the strengths of the tools at hand. We are now going to move from looking at the core tenants of programming design to some of the gritty details of enhancing your code with the power of Swift.

The first thing we will look at is making effective use of the associated value of an enumeration. Associated values are a pretty unique feature of Swift, so they open up some pretty interesting possibilities.

We have already seen in Chapter 3, One Piece at a Time – Types, Scopes, and Projects that we can use an enumeration with associated values to represent a measurement like distance in multiple measurement systems:

enum Height {
    case Imperial(feet: Int, Inches: Double)
    case Metric(meters: Double)
    case Other(String)
}

We can generalize this use case as using an enumeration to flatten out a simple class hierarchy. Instead of the enumeration, we could have created a height superclass or protocol with subclasses for each measurement system. However, this would be a more complex solution and we would lose the benefits of using a value type instead of a reference type. The enumeration solution is also very compact, making it very easy to understand at a glance instead of having to analyze how multiple different classes fit together.

Let's look at an even more complex example. Let's say we want to create a fitness app and we want to be able to track multiple types of workouts. Sometimes people workout to do a certain number of repetitions of various movements; other times they are just going for a certain amount of time. We could create a class hierarchy for this, but an enumeration with associated values works great:

enum Workout {
    case ForTime(seconds: Int)
    case ForReps(movements: [(name: String, reps: Int)])
}

Now, when we want to create a workout, we only need to define values relevant to the type of workout we are interested in without having to use any classes at all.

Another great use of enumerations with associated values is to represent the state of something. The simplest example of this would be a result enumeration that can either contain a value or an error description if an error occurs:

This allows us to write a function that can fail and give a reason that it failed:

This is an alternative to normal error handling and can make sense for functions where the failure case is treated similarly to a success case instead of as a rare exception.

A slightly more complex idea is to use an enumeration to represent a process that will go through various stages over time, often called a state machine. We could write an enumeration for the process of a download:

While the download is in progress we can access how complete it is and once it is complete we can access the data that it downloaded. This information is only accessible when it is applicable. This enumeration will also make it easier to make sure our download is always in a reasonable and clearly defined state. There is no possibility for a middle ground where, for example, the download might be complete but the data hasn't been processed yet. If we wanted to represent an additional processing step, we could easily add another case and it would be clear from then on out that a download will go through that additional state.