Skip to content
Theory · Enums · lesson 2 of 8

Raw values

In the previous lesson, the cases of an enum were only names from the menu. But a real menu also gives each dish a number: soup is first, salad is second, pasta is third. You can tell the waiter "number three, please", and they will know you mean the pasta. The number is printed on the menu once, and it is the same for everyone who orders the pasta.

You can give such numbers to the cases of an enum too:

SWIFT
enum Dish: Int {
    case soup = 1
    case salad
    case pasta
}

print(Dish.soup.rawValue) // 1
print(Dish.pasta.rawValue) // 3

Let's go through the program line by line:

  1. : Int after the name Dish says that each case has a number of type Int.
  2. case soup = 1 sets the soup's number explicitly.
  3. salad and pasta have no number written, and Swift counts on by itself: the salad gets 2, the pasta 3.
  4. Dish.pasta.rawValue reads the pasta's number, and the program prints 3.

The type and rawValue

A raw value is a constant value attached to a case right in the enum's declaration. Its type is written after the enum's name, following a colon: enum Dish: Int. This is not the same as CaseIterable from the previous lesson, but the two can be combined with a comma, the type first: enum Size: String, CaseIterable. You read a raw value through rawValue.

What to know about raw values:

  • all cases of one enum have raw values of the same type;
  • each case has one raw value, and it never changes: every Dish.pasta has the number 3;
  • without a type after the colon there are no raw values: with enum Drink { case tea, coffee }, the code Drink.tea.rawValue does not compile.

How Swift fills in values

If the raw values are whole numbers or strings, you do not have to write one for every case: Swift fills in the missing ones by itself. What exactly it fills in depends on the type.

Whole numbers

For Int, Swift counts in order:

  • if the first case has no number, it gets 0;
  • each next case without a number gets one more than the case before it;
  • if a case in the middle has a number written explicitly, Swift counts the cases after it from that number: in case pasta = 10, cake the cake gets 11.

Strings

For String, a case without an explicit value gets its own name:

SWIFT
enum Size: String {
    case small
    case medium = "M"
    case large = "L"
}

print(Size.small.rawValue) // small
print(Size.large.rawValue) // L

small has no string given, so its raw value is the case's name, "small". large has a string given explicitly—"L", like the size on the order ticket.

A case from a raw value

The waiter finds a dish by its number. Swift can do this too: it finds a case by its raw value. But the menu may have no number 7, so the result is an optional:

SWIFT
if let dish = Dish(rawValue: 2) {
    print(dish) // salad
}
let missing = Dish(rawValue: 7)
print(missing == nil) // true
let chosen = Dish(rawValue: 7) ?? .soup
print(chosen) // soup

Let's walk through the program:

  1. Dish(rawValue: 2) finds the case with the number 2 and returns a Dish? box with the salad inside. if let opens it, and the program prints salad.
  2. There is no case with the number 7, so Dish(rawValue: 7) returns nil.
  3. ?? supplies the default value, .soup, when the box is empty.

Swift adds this way of creating a value only to enums with raw values. The documentation writes it as init?(rawValue:), and the question mark there means the result is an optional.

Interviewers like to ask this: why does init?(rawValue:) return an optional? The answer: not every raw value has a case. You can pass any Int to Dish(rawValue:), but there are only three cases, so for every other number the result is nil, not a crash.

Here the menu picture is inaccurate. A real menu can be printed with a mistake, and two dishes end up with one number. Swift does not allow that: two equal raw values give the compile error raw value for enum case is not unique.

Common mistake

The result of Dish(rawValue:) was put into a constant of type Dish:

SWIFT
let dish: Dish = Dish(rawValue: 2) // ✗
TEXT
error: value of optional type 'Dish?' must be unwrapped to a value of type 'Dish'

The program does not compile: Dish(rawValue: 2) gives Dish?, not Dish. The compiler does not check that a case with the number 2 exists: it looks only at the type. Open the box with if let, or supply a default value with ??.

Key points

  • A raw value is attached to a case in the declaration: the type goes after the name, enum Dish: Int, and you read the value through rawValue.
  • Swift counts whole-number raw values by itself: from 0 or from an explicit number, each next one greater by one.
  • With String, a case without an explicit value gets its own name.
  • Dish(rawValue: 2) looks a case up by its raw value and returns an optional: there may be no such value.