Swift Enumerations Basics
Introduction
Enumerations (also known as enums) are a powerful feature in Swift that define a common type for a group of related values. They allow you to work with those values in a type-safe way within your code. Unlike enums in some other programming languages, Swift enumerations are first-class types that adopt many features traditionally supported only by classes.
In this tutorial, you'll learn how to:
- Define and use basic enumerations
- Work with enum case values
- Use switch statements with enums
- Understand raw values and associated values
- Apply enumerations in practical scenarios
What is an Enumeration?
An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code. Enumerations in Swift are much more flexible than their counterparts in C and many other languages.
Basic Syntax
Here's how to define a simple enumeration in Swift:
enum Direction {
case north
case south
case east
case west
}
You can also write the cases on a single line, separated by commas:
enum Direction {
case north, south, east, west
}
Each enumeration definition creates a brand new type. Like other types in Swift, their names should start with a capital letter.
Using Enumerations
Once you define an enumeration, you can create instances of it:
var currentDirection = Direction.north
When the type of the variable is already known, you can use a shorter dot syntax:
currentDirection = .east
Example: Direction Guidance
enum Direction {
case north, south, east, west
}
func provideDirections(heading: Direction) {
switch heading {
case .north:
print("Head north toward the mountain")
case .south:
print("Head south toward the lake")
case .east:
print("Head east toward the river")
case .west:
print("Head west toward the forest")
}
}
let myDirection = Direction.south
provideDirections(heading: myDirection)
// Output: Head south toward the lake
Matching Enum Values with Switch Statement
Enumerations work especially well with Swift's switch
statements:
let direction = Direction.west
switch direction {
case .north:
print("Heading north")
case .south:
print("Heading south")
case .east:
print("Heading east")
case .west:
print("Heading west")
}
// Output: Heading west
One important feature of Swift's switch
statement is that it must be exhaustive when used with enumerations. This means you must cover all possible cases, or include a default
case.
Enum with Raw Values
You can assign raw values to enumeration cases, which can be strings, characters, or any integer or floating-point type:
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
In this example, venus
has an implicit raw value of 2, earth
is 3, and so on.
You can access the raw value using the rawValue
property:
let earthOrder = Planet.earth.rawValue
print("Earth is planet number \(earthOrder) from the sun")
// Output: Earth is planet number 3 from the sun
You can also initialize an enum instance from a raw value:
if let possiblePlanet = Planet(rawValue: 4) {
print("The fourth planet is \(possiblePlanet)")
}
// Output: The fourth planet is mars
Example: HTTP Status Codes
enum HTTPStatus: Int {
case ok = 200
case created = 201
case accepted = 202
case noContent = 204
case badRequest = 400
case unauthorized = 401
case forbidden = 403
case notFound = 404
case serverError = 500
}
func handleResponse(statusCode: Int) {
guard let status = HTTPStatus(rawValue: statusCode) else {
print("Unknown status code: \(statusCode)")
return
}
switch status {
case .ok, .created, .accepted, .noContent:
print("Success with code \(status.rawValue)")
case .badRequest, .unauthorized, .forbidden, .notFound:
print("Client error with code \(status.rawValue)")
case .serverError:
print("Server error with code \(status.rawValue)")
}
}
handleResponse(statusCode: 200)
// Output: Success with code 200
handleResponse(statusCode: 404)
// Output: Client error with code 404
handleResponse(statusCode: 418)
// Output: Unknown status code: 418
String Raw Values
String raw values are particularly useful for enums:
enum Beverage: String {
case coffee = "Coffee"
case tea = "Tea"
case juice = "Juice"
case water = "Water"
}
print("I'd like a cup of \(Beverage.tea.rawValue), please.")
// Output: I'd like a cup of Tea, please.
If you don't specify raw values for string-based enums, Swift will use the case name as the raw value:
enum Direction: String {
case north, south, east, west
}
print(Direction.south.rawValue) // Output: south
Associated Values
Swift enumerations can also store associated values of any type. This allows you to attach additional information to enum cases:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
With this, you can create barcodes of either type:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
You can extract the associated values using a switch statement:
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case .qrCode(let productCode):
print("QR code: \(productCode)")
}
// Output: QR code: ABCDEFGHIJKLMNOP
Example: Result Type
Associated values are commonly used in Swift's error handling pattern:
enum NetworkResult {
case success(Data)
case failure(Error)
}
func fetchData(completion: (NetworkResult) -> Void) {
// Simulate network request
if Bool.random() {
let data = "Response data".data(using: .utf8)!
completion(.success(data))
} else {
struct NetworkError: Error {
let message: String
}
completion(.failure(NetworkError(message: "Failed to fetch data")))
}
}
fetchData { result in
switch result {
case .success(let data):
if let string = String(data: data, encoding: .utf8) {
print("Received: \(string)")
}
case .failure(let error):
print("Error: \(error)")
}
}
// Output will be either:
// Received: Response data
// OR
// Error: NetworkError(message: "Failed to fetch data")