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")
Practical Applications
Example 1: App Navigation
enum NavigationDestination {
case home
case profile(userId: String)
case settings
case article(id: Int)
}
func navigate(to destination: NavigationDestination) {
switch destination {
case .home:
print("Navigating to Home screen")
case .profile(let userId):
print("Navigating to Profile for user \(userId)")
case .settings:
print("Navigating to Settings")
case .article(let id):
print("Navigating to Article #\(id)")
}
}
navigate(to: .home)
// Output: Navigating to Home screen
navigate(to: .profile(userId: "user123"))
// Output: Navigating to Profile for user user123
navigate(to: .article(id: 42))
// Output: Navigating to Article #42
Example 2: Pricing Strategy
enum Pricing {
case fixed(Double)
case variable(base: Double, perUnit: Double)
case free
func calculate(units: Int = 1) -> Double {
switch self {
case .free:
return 0
case .fixed(let price):
return price
case .variable(let base, let perUnit):
return base + (perUnit * Double(units))
}
}
}
let subscriptionPlan = Pricing.fixed(9.99)
print("Monthly subscription: $\(subscriptionPlan.calculate())")
// Output: Monthly subscription: $9.99
let electricityBill = Pricing.variable(base: 10.0, perUnit: 0.12)
print("Electricity bill for 100 kWh: $\(electricityBill.calculate(units: 100))")
// Output: Electricity bill for 100 kWh: $22.0
let freeTrial = Pricing.free
print("Free trial cost: $\(freeTrial.calculate())")
// Output: Free trial cost: $0.0
Summary
Swift enumerations are powerful, flexible, and type-safe. They allow you to:
- Define a group of related values as a type
- Work with those values in a type-safe way
- Associate raw values to enum cases for easy conversion
- Attach additional information using associated values
- Use pattern matching to extract associated values
- Create enums with methods and properties (covered in more advanced tutorials)
Enumerations are an essential tool for Swift developers, helping to write more expressive, safer code that's easier to understand and maintain.
Exercises
- Create an enum called
Day
representing days of the week with appropriate raw values. - Create an enum called
Measurement
with cases for different units (e.g.,centimeters
,inches
) that can store a value. - Implement an enum for representing results of a login operation with appropriate associated values for success and different types of failure.
- Create an enum for different payment methods (cash, credit card, digital wallet) with appropriate associated values.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)