Ejiro Asiuwhu Software developer with industry experience in building scalable and performant applications that run on the web and smartphones with cutting-edge technology.

Swift enums: An overview with examples

5 min read 1558

Swift Logo

Enumerations (or enums for short) in Swift define a common type for a group of related values. According to the Swift documentation, enums enable you to work with those values in a type-safe way within your code. Enums come in particularly handy when you have a lot of different options you want to encode.

Using enum in Swift is similar to writing code that returns a boolean — e.g., true or false — when a condition is met. In the case of enums, however, you can have more than two possible values.

Think of an enum like a thermostat. There is a range of values that could possibly match the outcome — e.g., low, medium, high, extreme. Depending on the case that is matched, we’ll want to run some specified code.

An enum is a special type of variable that is specifically used with switch and conditionals. Swift enumeration cases don’t have unique default integer values (like an array), unlike languages such as TypeScript and Objective C where the first element has a value of 0, the second a value of 1, and so on.

In this tutorial, we’ll cover all the basics of Swift enums, including:

Swift enum syntax

To define an enum in Swift, use the keyword enum followed by the name of the enum. The name of an enum in Swift should follow the PascalCase naming convention in which the first letter of each word in a compound word is capitalized.

// starts with the keyword 'enum'and follows the PascalCase naming convention
enum EnumerationNmae {
    // our enum values (case) goes in here
  }

Here is a closer look at how to declare values in an enum:

enum Direction {
    case up
    case down
    case left
    case right
}

The values declared in an enum — up, down, left and right — are referred to as enumeration case. We use the case keyword to introduce a new enumeration case.

Enums are particularly useful inside switch statements, as opposed to an if-else statement. That’s because in a switch statement Swift knows all the values the enum holds, so it will ensure you cover all the cases in your enum or add a default case.

CaseIterable in enumeration cases

CaseIterable is a type that provides a collection of all the values of an enumeration. It’s used to iterate over all the cases in an enum.

We made a custom demo for .
No really. Click here to check it out.

To do this, add CaseIterable after the name of the enum. With that in place, Swift will give us access to a collection of all the cases through a property on the enumeration type called allCases.

enum CarBrand: String, CaseIterable {
    case Mercedes = "Known for luxury and iconic design. Definitely my fav!"
    case Toyota = "Known for high-quality, high-value cars"
    case Volkswagen = "This is the people's car"
    case Ford = "Known for crisp handling, absorbent ride, and solid feel"
    case Honda = "Known for their well-built, reliable cars"
}

To access the collection of all the cases in our CarBrand enum, we can do this:

print(CarBrand.allCases.count)
// expected output: 5

In the example above, we wrote CarBrand.allCases to access a collection that contains all of the cases of the CarBrand enumeration. The count method gives the number of elements in our collection.

We can go further by using a for loop over all the cases in our enum.

Let’s print put the raw value in our enumeration cases using the allCases method:

// create an enum with a CaseIterable type

enum CarBrand: String, CaseIterable {
    case Mercedes = "Known for luxury and iconic design. Definitely my fav!"
    case Toyota = "Known for high-quality, high-value cars"
    case Volkswagen = "This is the people's car"
    case Ford = "Known for crisp handling, absorbent ride, and solid feel"
    case Honda = "Known for their well-built, reliable cars"
}

// we are looping through our enum and accessing all its raw values
for brand in CarBrand.allCases {
    print(brand.rawValue)
}
// expected output:
// Known for luxury and iconic design. Definitely my fav!
// Known for high-quality, high-value cars
// This is the people's car
// Known for crisp handling, absorbent ride, and solid feel
// Known for their well-built, reliable cars

Enum raw values

In our enum, we can declare a raw value type. This essentially means attaching a value to the enum case.

To better understand raw values in enums, let’s create an enum of type string (it can be any type) to hold different brands of cars along with attributes for which each brand is known (these will be the raw values):

// Enum with raw values

enum CarBrand: String {
    case Mercedes = "Known for luxury and iconic design. Definitely my fav!"
    case Toyota = "Known for high-quality, high-value cars"
    case Volkswagen = "This is the people's car"
    case Ford = "Known for crisp handling, absorbent ride, and solid feel"
    case Honda = "Known for their well-built, reliable cars"
}

To set a value to your enum, you need to assign a data type to it. In our case above, we are using a type of String.

Each raw value for our enum case must be a unique string, character, or value of any integer or floating-point type. This means the value for the two case statements cannot be the same. See the code below:

enum CarBrand: String {
    case Toyota = "High value cars."
    case Volkswagen = "High value cars."
}

The code above throws an error of “Raw value for enum case is not unique” because both case values are exactly the same.

Now that we’ve created our enum with each enumeration case having a raw value, let’s create a function that will return the rawValue of the various car brands. The raw values here represent the attributes each brand is known for:

func carKnownFor(brand: CarBrand) -> String {
    return brand.rawValue
} 

carKnownFor(brand: .Ford)
carKnownFor(brand: .Mercedes)

// expected output: 
// "Known for luxury and iconic design. Definitely my fav!"
// "Known for crisp handling, absorbent ride, and solid feel"

Enum associate values

One of the best features of enumerations in Swift is that you can have values that you define attach to enums in each case. This enables you to attach additional information to your enums so they can represent more meaningful data.

For example, let’s say we have an enum that defines the prices of Mercedes cars. Based on the price, we want to determine whether a user can afford the model of car or not. We can associate a price with the brand of car in our enum as we have in the example below:

// enum with associated values
enum MercedesModelPrice {
   case MayBach(price: Int)
   case AMG_GT(price: Int)
   case Metris(price: Int)
   case SprinterWagon(price: Int)
   case GClass
}
// enum without associated values 
enum MercedesModelPrice {
   case MayBach
   case AMG_GT
   case Metris
   case SprinterWagon
   case GClass
}

Now, let’s create a function to check whether a user can afford a Mercedes:

func getMercedesPrice(for mercedesModel: MercedesModelPrice) {
   switch mercedesModel {
   case .MayBach(price: let price) where price >= 100_000:
    print("You just bought yourself a new car")

   case .Metris(price: let price) where price >= 86_000:
    print("You just bought yourself a new car")

   case .AMG_GT(price: let price)  where price >= 74_000:
    print("You just bought yourself a new car")

   case .SprinterWagon(price: let price) where price >= 45_000:
     print("You just bought yourself a new car")

   case .GClass, .SprinterWagon, .Metris, .AMG_GT, .MayBach:
    print("Insufficient funds. You cant' afford this car at the moment")

   }
}

// Calling our function
getMercedesPrice(for: .SprinterWagon(price: 200_000))
// expected output: You just bought yourself a new car

Notice how we’re using the where keyword to filter the case for a specific price.

It’s worth noting that the order of the case statement matters. Putting the last case statement (case .GClass, .SprinterWagon, .Metris, .AMG_GT, .MayBach:) first means we’ll always get the result attached to this case as a match.

Enum methods

Apart from defining enumeration cases in our enum, we can also define methods in our enum, like this:

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

    func dayType() -> String {
        switch self {
        case .Sunday, .Saturday:
            return  "Weekend"
        default:
            return "Weekday"
        }
     }
}


Weekday.Monday.dayType()
// this will return "Weekday"
Weekday.Sunday.dayType()
// this will return "Weekend"

In the code snippet above, we’re using the switch statement within the dayType method to return Weekend or Weekday as the output depending on the day of the week.

Computed properties in Swift enums

You can also use computed properties in enums in place of functions.

Let’s replace the dayType function we have in our code above with a computed property:

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

    var dayType: String {
        self == .Saturday || self == .Sunday ? "Weekend" : "Weekday"
    }

}

Weekday.Monday.dayType
// this will return "Weekday"
Weekday.Sunday.dayType
// this will return "Weekend"
>

Conclusion

Swift enums can be a powerful way to simplify your code. In this tutorial, we covered the syntax of enums and how to define them, how to create enumeration cases, and how to use them in switch statements. We also defined CaseIterable as a type that provides a collection of all the values of the enumeration. We covered enum values (both raw and associated values) and how to use them in our codebase. Finally, we demonstrated how to use computed properties in Swift enums in place of functions.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Ejiro Asiuwhu Software developer with industry experience in building scalable and performant applications that run on the web and smartphones with cutting-edge technology.

Leave a Reply