Quick Tip: Enumerations in Swift

Enumerations are a common design pattern in many programming languages. While you may be familiar with enumerations in C and Objective-C, Swift’s implementation of enumerations is significantly more powerful and flexible. In this quick tip, you’ll learn what’s special about enumerations in Swift, how to use them in your projects, and what makes them so powerful.

Quick Tip: Enumerations in Swift

1. What Is an Enumeration?

Enumerations aren’t new and they’re certainly not unique to Swift. However, if you’re familiar with enumerations in C, then you’re going to love Swift’s powerful take on enumerations.

If enums or enumerations are new to you, then you may not be familiar with what they have to offer. In Swift, enumerations are first class types that define a list of possible values for that type.

An example might be the possible states of a network connection. The possible states could be:

  • disconnected
  • connecting
  • connected

We could add a fourth state for the case the state is unknown. With this example in mind, let’s see how to define and implement such an enumeration.

Basics

Like I said, enumerations are first class types in Swift. An enumeration definition looks very similar to a class or structure definition. In the example below, we define the ConnectionState enumeration.

enum ConnectionState {
    
}

The name of the enumeration is preceded by the enum keyword and followed by a pair of curly braces. The ConnectionState enumeration will define the possible states of a network connection. To define these states, we add member values or members to the enumeration’s definition. The definition of a member value always starts with the case keyword.

enum ConnectionState {
    case Unknown
    case Disconnected
    case Connecting
    case Connected
}

In C or Objective-C, the above enumeration would look a bit different as illustrated in the example below. Each value of the enumeration corresponds with an integer, for example, ConnectionStateUnknown equals 0, ConnectionStateDisconnected equals 1, etc.

typedef enum : NSUInteger {
    ConnectionStateUnknown,
    ConnectionStateDisconnected,
    ConnectionStateConnecting,
    ConnectionStateConnected
} ConnectionState;

This isn’t true in Swift. The members of an enumeration don’t automatically correspond with an integer value. The members of the ConnectionState enumeration are values themselves and they are of type ConnectionState. This makes working with enumerations safer and more explicit.

Raw Values

It is possible to explicitly specify the values of the members of an enumeration. In the following example, the members of the ConnectionState enumeration have a raw value of type Int. Each member is assigned a raw value, corresponding with an integer.

enum ConnectionState: Int {
    case Unknown = -1
    case Disconnected = 0
    case Connecting = 1
    case Connected = 2
}

Note that we specify the type of the raw values in the enumeration’s definition and that no two member values can have the same raw value. If we only specify a value for the Unknown member, then Swift will automatically increment the value of the Unknown member and assign unique values to the other members of the enumeration. To better illustrate this, the below example is identical to the previous definition of the ConnectionState enumeration.

enum ConnectionState: Int {
    case Unknown = -1
    case Disconnected
    case Connecting
    case Connected
}

2. Working with Enumerations

Initialization

Using the ConnectionState enumeration is similar to using any other type in Swift. In the next example, we declare a variable, connectionState, and set its value to ConnectionState.Connecting.

var connectionState = ConnectionState.Connecting

The value of connectionState is ConnectionState.Connecting and the variable is of type ConnectionState.

Swift’s type inference is very convenient when working with enumerations. Because we declared connectionState as being of type ConnectionState, we can now assign a new value by using the shorthand dot syntax for enumerations.

connectionState = .Connected

Control Flow

Using enumerations in an if or switch statement is straightforward. Remember that switch statements need to be exhaustive. Add a default case if necessary.

enum ConnectionState {
    case Unknown
    case Disconnected
    case Connecting
    case Connected
}

var connectionState = ConnectionState.Connecting

connectionState = .Connected

switch connectionState {
    case .Disconnected:
        println("Disconnected")
    case .Connecting:
        println("Connecting")
    case .Connected:
        println("Connected")
    default:
        println("Unknown State")
}

The following example demonstrates how the ConnectionState enum can be used. It also shows how to access the associated value of an enum member. The canConnect function accepts a ConnectionState instance and returns a Bool.

func canConnect(connectionState: ConnectionState) -> Bool {
    var result = false
    
    switch connectionState {
        case .Connected(let port):
            if port == 3000 {
                result = true
            }
        default:
            result = false
    }
    
    return result
}

let state = ConnectionState.Connected(3000)

if canConnect(state) {
    // ...
}

The canConnect function only returns true if the ConnectionState instance passed to the function is equal to .Connected and its associated value is an Int equal to 3000. Note that the associated value of the Connected member is available in the switch statement as a constant named port, which we can then use in the corresponding case.

3. Associated Values

Another compelling feature of Swift enums are associated values. Each member of an enum can have an associated value. Associated values are very flexible. For example, associated values of different members of the same enum don’t need to be of the same type. Take a look at the following example to better understand the concept of associated values.

enum ConnectionState {
    case Unknown
    case Disconnected
    case Connecting(Int, Double)
    case Connected(Int)
}

The Unknown and Disconnected members don’t have an associated value. TheConnecting member has an associated value of type (Int, Double), specifying the port number and timeout interval of the connection. The Connected member has an associated value of type Int, specifying the port number.

It’s important to understand that an associated value is linked to or associated with a member of the enumeration. The member’s value remains unchanged. The next example illustrates how to create a ConnectionState instance with an associated value.

var connectionState = ConnectionState.Connecting(3000, 30.0)

4. Methods and Value Types

Methods

Enumerations are pretty powerful in Swift. Enumerations can even define methods, such as an initializer to select a default member value if none was specified.

enum ConnectionState {
    case Unknown
    case Disconnected
    case Connecting(Int, Double)
    case Connected(Int)
    
    init () {
        self = .Unknown
    }
}

var connectionState = ConnectionState() // .Unknown

In this example, we initialize an instance of the ConnectionState enumeration without explicitly specifying a value for it. In the initializer of the enumeration, however, we set the instance to Unknown. The result is that the connectionState variable is equal to ConnectionState.Unknown.

Value Types

Like structures, enumerations are value types, which means that an enumeration is not passed by reference, like class instances, but by value. The following example illustrates this.

var connectionState1 = ConnectionState()
var connectionState2 = connectionState1

connectionState1 = .Connected(1000)

println(connectionState1) // .Connected(1000)
println(connectionState2) // .Unknown

Even though we assign connectionState1 to connectionState2, the values of connectionState1 and connectionState2 are different at the end of the example.

When connectionState1 is assigned to connectionState2, Swift creates a copy of connectionState1 and assigns that to connectionState2. In other words, connectionState1 and connectionState2 refer to two different ConnectionState instances.

Conclusion

Enums in Swift are incredibly powerful compared to, for example, enums in C. One of the most powerful aspects of enumerations is that they are a first class types in Swift. Type safety is a key aspect of the Swift language and enumerations fit perfectly in that mindset.