Understanding Swift Enumerations

Enumerations define a type to represent a group of related values. Swift enumerations are flexible, powerful and type safe.


An enumeration is defined using the enum keyword. Here's an enumeration called Day representing the days of a week.

enum Day {
    case Sunday
    case Monday
    case Tuesday
    case Wednesday
    case Thursday
    case Friday
    case Saturday

The values in the enumeration (such as Sunday, Monday, ... etc.) are called the members of the enumeration.

The case keyword indicates the start of a new line of member definitions. Multiple members can be defined in a single line by separating them with commas.

The Day enumeration could have also been defined as:

enum Day {
    case Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday

Naming Conventions

Enumerations define a new type, so by convention their names should start with an uppercase letter and the first letter of each subsequent word should also be capitalized. For example, Month, TaskStatus, Planet.

The enumeration members should also use the same naming convention. For example, January, February, Queued, Completed, Earth, Mars.

Enumerations should also be given singular names as it makes the code easier to read. TaskStatus.Completed rolls off the tongue more easily than TaskStatuses.Completed.

Enumerations are First-Class Types

Unlike C and Objective-C, enumeration members in Swift are not given an implicit integer type and assigned default integer values such as 0, 1, 2 and so on. Enumerations in Swift are first-class types and each member's type is the enumeration to which it belongs. In the example above, the members Sunday, Monday, Tuesday, ..., etc are all of type Day.

Using enumerations

An enumeration variable can be declared like this:

var tomorrow = Day.Monday

The type of tomorrow is inferred to be Day and subsequent assignments to this variable can use a more compact syntax by omitting the type name.

tomorrow = .Tuesday

This compact syntax can be used wherever the type of the variable is already known or can be inferred from the context.

In the example below, weekend is an array with elements of type Day, so the type name can be left out from the append() statements that follow.

var weekend = [Day]() 
weekend.append(.Saturday) // Type name left out
weekend.append(.Sunday)   // Type name left out

Another example of the compact syntax can be seen in calls to functions that take enumeration parameters.

func dayAfter(day: Day) -> Day {
    // ...

let nextDay = dayAfter(.Wednesday) // Type name left out

Enumerations and the switch statement

The switch statement can be used to match enumeration values.

Here's the full implementation of the dayAfter() function that uses a switch statement to return the day that comes after a given day.

func dayAfter(day: Day) -> Day {
    switch day {
    case .Sunday:
        return .Monday
    case .Monday:
        return .Tuesday
    case .Tuesday:
        return .Wednesday
    case .Wednesday:
        return .Thursday
    case .Thursday:
        return .Friday
    case .Friday:
        return .Saturday
    case .Saturday:
        return .Sunday

As required by the switch statement in Swift, the case statements must cover all possible cases. If in a particular switch statement, it is not relevant to provide all possible cases, a default statement must be used to handle the cases that are not provided.

Associated Values

Enumeration members in Swift are typed values on their own, that can be assigned to variables or constants, matched with switch statements, passed as function parameters, appended to arrays and in general used like values of other types.

It is also possible to store one or more associated values with each enumeration member. These associated values can be of any type and can be different for each enumeration member.

Let us look at an example to understand this concept better. Suppose we want to track the status of tasks in a task scheduling system. Tasks may have different statuses such as queued, in progress, failed and completed. Statuses such as queued and completed do not need any further information to convey their meaning. But for a task in progress, we may want to know the how much of the task has been completed and how much longer it will take to fully complete it. Also when a task fails we would surely be interested in knowing the cause of failure.

Here's how we could represent this using enumerations.

enum TaskStatus {
    case Queued
    case InProgress(percentComplete: Int, timeRemaining: Int)
    case Completed
    case Failed(error: String)

This defines the InProgress member to have two associated values of type Int and the Failed member to have an associated value of type String. The Queued and Completed members do not have any associated values.

The TaskStatus enumeration can be used in the following manner:

var status1 = TaskStatus.Queued
var status2 = TaskStatus.InProgress(percentComplete: 54, timeRemaining: 5600)
var status3 = TaskStatus.InProgress(percentComplete: 95, timeRemaining: 1200)
var status4 = TaskStatus.Failed(error: "Network not available.")
var status5 = TaskStatus.Failed(error: "The widget blew up.")
var status6 = TaskStatus.Completed

Each of the status variables (status1 ... status6) has been assigned a TaskStatus member. status2 and status3 are both assigned TaskStatus.InProgress but with different associated values. And similarly status4 and status5 are both assigned TaskStatus.Failed with different associated values.

The variables can be reassigned different values as shown here:

status2 = .InProgress(percentComplete: 86, timeRemaining: 2100)
status3 = .Completed

The associated values can be extracted in a switch statement as constants (with let) or variables (with var).

switch status4 {
case .Queued:
    print("We have a long wait ahead.")
case .InProgress(let percentComplete, var timeRemaining):
    timeRemaining = timeRemaining / 1000
    print("We are \(percentComplete)% through. " +
          "Expected to complete in \(timeRemaining) secs.")
case .Completed:
    print("Yay! We are done.")
case .Failed(let error):
    print("We are in trouble. \(error)")

Raw Values

Associated values are a very powerful and flexible way to store different values with enumeration members. But sometimes we don't need the full flexibility or power of associated values. Swift provides an alternative way of assigning values with enumeration members. Enumeration members can be assigned default values called raw values. Raw values can be strings, characters, integers or floating-point types.

The code below shows an enumeration that stores raws values of type String with each enumeration member.

enum Emoticon: String {
    case Smile = ":-)"
    case Wink  = ";-)"
    case Laugh = ":D"
    case Frown = ":-("

var e = Emoticon.Smile
e = .Laugh

The raw value of an enumeration member can be accessed from it's rawValue property.

print(Emoticon.Smile.rawValue)  // prints :-)

Raw Value Restrictions

Raw values come with the following restrictions:

  • All raw values must be of the same type.
  • Raw values must be literals.
  • There must be only one raw value for each enumeration member.
  • Raw values must be unique for each enumeration member.
  • The raw value for each enumeration member is assigned at enumeration definition time.
  • The raw value always remains the same and cannot be set to a different value when creating a constant or variable based on the enumeration.

Integer Raw Values

When the raw values of an enumeration are integers, they will auto-increment if no value is specified for some members.

enum Month: Int {
    case January = 1, February, March, April,
         May, June, July, August, September,
         October, November, December

print(Month.July.rawValue)      // prints 7

If no value is specified for the first member then the values start at 0.

enum Month: Int {
    case January, February, March, April,
         May, June, July, August, September,
         October, November, December

print(Month.July.rawValue)      // prints 6

This usage of Swift enumerations is very reminiscent of the enumerations in C and Objective-C.

Raw Value Initializers

Enumerations with raw values automatically get an initializer that can be used to initialize the enumeration from its raw value. The initializer takes a single parameter of the raw value type and returns the enumeration member with matching raw value or nil if no enumeration with matching value is found.

enum Month: Int {
    case January = 1, February, March, April,
         May, June, July, August, September,
         October, November, December

let month = Month(rawValue: 7) 
// The type of month is Month? and its value is Month.July.

let someMonth = Month(rawValue: 14)
// The type of month is Month? and its value is nil.

Further Reading

Swift Enumerations are first-class types. They support many more features such as computed properties, instance methods and initializers. In addition to these, they can be extended and can also conform to protocols. We will explore these advanced features of enumerations in future posts.