Skip to content
Theory · Enums · lesson 6 of 8

Enum methods

On the menu, each line has its price printed next to it. To find it out, you do not need to go to the till: a look at the menu line itself is enough. The answer lives in the same place as the drink.

In the first lesson, a separate function price(of:) worked out the price, and you passed it the drink. You can also keep code about drinks inside the enum itself. Then a case answers a question about itself:

SWIFT
enum Drink {
    case tea, coffee

    func price(cups: Int) -> Int {
        switch self {
        case .tea:
            return 2 * cups
        case .coffee:
            return 3 * cups
        }
    }
}

let drink = Drink.coffee
print(drink.price(cups: 2)) // 6

Let's go through the program:

  1. The function price(cups:) is declared inside the enum's braces. Such a function is a method of the enum, like hasSuffix on a string.
  2. drink.price(cups: 2) calls the method with a dot on the value drink.
  3. Inside the method, self is the value the method was called on. Here that is drink, which is coffee.
  4. switch self picks the .coffee branch, and the method returns 3 * 2, which is 6.

A method and self

A method is declared the same way as an ordinary function, only inside the enum, usually after the list of cases. What to know about a method:

  • you call it on a value with a dot: drink.price(cups: 2) or Drink.tea.price(cups: 3);
  • self inside the method is that value itself, "this line of the menu";
  • switch self is the usual body of such a method: each case gets its own branch, and default is not needed.

Computed property

A string has count: you read it with a dot and no parentheses. What you read on a value with a dot and no parentheses is called a property. You can give an enum a property too, only your own code will compute it.

Now let the coffee ticket record the number of cups. The price of the order depends on that number:

SWIFT
enum Order {
    case tea
    case coffee(cups: Int)

    var price: Int {
        switch self {
        case .tea:
            return 2
        case .coffee(let cups):
            return cups * 3
        }
    }
}

print(Order.coffee(cups: 2).price) // 6

A computed property is a property whose value is not stored anywhere but computed anew on every read. Its declaration has three parts:

  • var—a computed property is declared only with var: with let the code does not compile;
  • price: Int—the property's name and type;
  • the braces—the code that returns the value with return.

You read the property without parentheses: .price, not .price(). In the .coffee(let cups) branch, the pattern gets the number of cups out, and for two cups the property gives 6.

A short branch can also be written on one line: case .tea: return 2. It is the same branch, only without the line break.

When do you write a method, and when a property? A property fits when the answer needs nothing but the value itself: the order's price, the cup's size. A method is needed when the answer needs data from outside, like the number of cups in price(cups:).

Here the menu picture is inaccurate. A menu prints the price once, but a computed property stores nothing: price runs its switch on every read.

Common mistake

The method was called on the type itself, not on a value:

SWIFT
print(Drink.price(cups: 2)) // ✗
TEXT
error: instance member 'price' cannot be used on type 'Drink'; did you mean to use a value of this type instead?

The program does not compile. Drink is the whole menu, not one drink, and it is unclear whose price to work out: the tea's or the coffee's. The compiler suggests that a value of this type is needed. Call the method on a case, Drink.tea.price(cups: 2), or on a constant: drink.price(cups: 2).

Key points

  • An enum's method is declared inside its braces and called on a value with a dot: drink.price(cups: 2).
  • self inside a method is the value it was called on, and switch self picks a branch by case.
  • A computed property stores nothing: its code runs on every read, and you read it without parentheses.