Skip to content

learn-co-curriculum/swift-enumerations-reading

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Enumerations

NameOfPerson

Tweet ~Bird

Overview

We'll cover enums which introduce a new type and are very useful in Swift programming when you're working with a limited set of values.

Learning Objectives

  • Define enums, and distinguish them from classes
  • Explain the syntax of enums
  • Use enums within functions
  • Add computed properties and methods to enums

Directions

Imagine you're creating a simple top-down game where the player controls a little character that can move around a map. It's a pretty simple game, so the character can only move up, down, left, or right. You probably have some sort of game controller that includes a method that could look something like this:

func playerDidMove(_ direction: String) {
    print("Player moved \(direction)")
}

playerDidMove("up")
// prints "Player moved up"

Great! The problem is, some directions are invalid. What if a player tries to move diagonally?

func playerDidMove(_ direction: String) {
    print("Player moved \(direction)")
}

playerDidMove("diagonally up")
// prints "Player moved diagonally up"

Oh no! The game doesn't actually support diagonal movement, so in your game, you probably haven't handled this movement direction. It would make sense, then, to check the direction parameter to make sure the directions are valid. You could modify playerDidMove(_:) to check the String that is passed in:

func playerDidMove(_ direction: String) {
    if direction == "up" ||
       direction == "down" ||
       direction == "left" ||
       direction == "right" {
        print("Player moved \(direction)")
    } else {
        print("Oops! That direction doesn't make sense: \(direction)")
    }
}

playerDidMove("up")
// prints "Player moved up"
playerDidMove("diagonally left")
// prints "Oops! That direction doesn't make sense: diagonally left

Perfect! Let's ship the code.

Except...what if you call playerDidMove(_:) with the wrong case?

playerDidMove("Up")
// prints "Oops! That direction doesn't make sense: Up

Or what if you make a typo when you call playerDidMove(_:)?

playerDidMove("dwn")
// prints "Oops! That direction doesn't make sense: dwn

It's a little bit ridiculous for playerDidMove(_:) to handle possible typos, even the most common ones, but it's also going to be time-consuming—not to mention error-prone—to ensure that you always call playerDidMove(_:) with the correct strings.

Luckily, Swift has a solution to this problem: Enumerations (or enums for short).

Enumerations

Instead of using strings, it would be nice to create your own type in which you can specify the allowed values: left, up, down, or right. That, in fact, is exactly what Swift's enums allow you to do. Given a specific, finite set of values, you can create your own type, through enums, to ensure that one of those values is always used. Selecting only one of a limited number of possible values is ensured through Swift's type system, so you can never specify an incorrect value (like you can if you just use plain strings).

Creating an enum to specify directions is pretty easy:

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

That looks a bit like a class definition, doesn't it? The main difference is that the type is declared using the enum keyword instead of class. Furthermore, in the body, the allowed types are specified using the case keyword.

This declaration introduces a new type called Direction. Direction constants and variables can have one of four values: up, down, left, and right. It cannot contain any other values, which ensures that you never use an invalid Direction anywhere.

Declaring constants and values of type Direction isn't different from any other declaration. Here's how you can declare a constant called direction1 and assign the Direction value up:

let direction1 = Direction.up

Notice that direction1 is assigned the value Direction.up. Direction.up is the full name of the up value.

That's a lot to type, so Swift allows a shorthand: If you explicitly declare the type of the constant or variable, you can leave off the enum's name (Direction) when assigning that value:

let direction2: Direction = .down

Since Swift knows direction2 is of type Direction, you can simply assign the value .down.

You can see how this works in the playground file included in this repo. Start creating a constant. You can see as you type Direction., the valid values will pop up in Xcode's autocomplete:

Option-clicking on the constant also shows that the type is inferred to be Direction:

Using Enums

Let's rewrite playerDidMove(_:) so it takes a Direction parameter, instead of a String. Your new function definition might look like this:

func playerDidMove(_ direction: Direction) {
    print("Player moved \(direction)")
}

That's much less code! Thanks to the Direction enum, you know you won't be getting an invalid value. The Swift compiler won't even let you write code that passes an invalid Direction.

Calling this function is pretty easy:

playerDidMove(.left)
// prints "Player moved left"

Notice, again, that you can just pass .left instead of Direction.left, since Swift knows that the parameter is of type Direction.

You can also pass constants and variables of type Direction as you'd expect:

let direction1 = Direction.up
let direction2: Direction = .down
var direction3: Direction = .right

playerDidMove(direction1)
// prints "Player moved up"
playerDidMove(direction2)
// prints "Player moved down"
playerDidMove(direction3)
// prints "Player moved right"

Properties and Methods

Enums can have methods and properties, just like classes. One gotcha, though: Enums can only have computed properties, not stored properties.

Properties and methods on enums can be useful. Imagine you wanted playerDidMove(_:) to print a more exciting message, like "Player moved RIGHT!" By default, the string that represents an enum is just the name of the value, but you can add a property to make it look more exciting, like an all-caps version.

Adding a computed property or method to an enum is the same as adding a property or method to a class:

enum Direction {
    case up
    case down
    case left
    case right

    var excited: String {
        switch self {
        case .up:
            return "UP"
        case .down:
            return "DOWN"
        case .left:
            return "LEFT"
        case .right:
            return "RIGHT"
        }
    }
}

The above block of code added a computed property called excited to Direction. excited returns a string that describes the direction in all capitals. Let's break down what that property is doing.

First of all, you should notice that it uses a switch statement to enumerate all the values of the enum (see why it's called an enumeration now?). It switches on self, which is the enum value on which the property is being called. The cases of the switch statement are each possible value of the enum. Notice that there isn't a default case: Since the Swift compiler knows what all the possible values of the Direction enum are, it also knows that all possible enum values have been accounted for, so a default case is unnecessary and superfluous.

You can then modify playerDidMove(_:) to call the excited property:

func playerDidMove(direction: Direction) {
    print("Player moved \(direction.excited)")
}

playerDidMove(.left)
// prints "Player moved LEFT"

Enums are very useful in Swift programming when you have a limited set of values that you want to deal with. Enums introduce a new type, and the Swift compiler will ensure that you only use the possible values of that type.

View this lesson on Learn.co

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages