Skip to main content

Working with Enumerations

1. Defining Enumerations

Feature Description Example
Basic Enum Defines a set of named constants.
enum CompassPoint {
    case north
    case south
    case east
    case west
}
Raw Values Assigns a value to each case, such as String or Int.
enum Planet: Int {
    case mercury = 1
    case venus = 2
    case earth = 3
}
Associated Values Each case can hold associated values of any type.
enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}
Implicit Cases Case values are implicitly provided if not specified.
enum Color {
    case red
    case green
    case blue
}

Example: Using Raw Values

enum Direction: String {
    case left = "turnLeft"
    case right = "turnRight"
}

let direction = Direction.left
print(direction.rawValue) // Output: turnLeft

Example: Using Associated Values

enum PaymentMethod {
    case creditCard(String, Int)
    case bankTransfer(String)
}

let payment = PaymentMethod.creditCard("1234", 1234)
switch payment {
case .creditCard(let number, let cvv):
    print("Card: \(number), CVV: \(cvv)")
case .bankTransfer(let account):
    print("Account: \(account)")
}

2. Using Raw Values

Enumeration Raw Value Type Raw Values
Color String "red", "green", "blue"
ErrorCode Int 100, 200, 404

Example: Accessing Raw Values

enum Color: String {
    case red = "red"
    case green = "green"
    case blue = "blue"
}

let color = Color.red
print(color.rawValue) // Output: red

Example: Initializing from Raw Values

enum ErrorCode: Int {
    case success = 200
    case notFound = 404
    case serverError = 500
}

if let code = ErrorCode(rawValue: 404) {
    print("Error: \(code)") // Output: Error: notFound
}
Note: Raw values must be unique within the enumeration and are assigned at compile time.

3. Using Associated Values

Associated values allow you to attach data of any type to each case of an enumeration. This is particularly useful for representing variants of a type that carry additional information.

Case Associated Value Type Description
success String Represents a successful operation with a message.
failure Error Represents a failed operation with an error object.

Example: Defining an Enum with Associated Values

enum NetworkResult {
    case success(String)
    case failure(Error)
}

let result = NetworkResult.success("Data loaded successfully")

Example: Using Associated Values in a Switch Statement

func handleResult(_ result: NetworkResult) {
    switch result {
    case .success(let message):
        print("Success: \(message)")
    case .failure(let error):
        print("Failure: \(error.localizedDescription)")
    }
}

handleResult(NetworkResult.success("Loaded"))

4. Matching Enumeration Values

Pattern Description Usage
switch Matches enumeration values with exhaustive cases
switch color {
case .red: print("Red")
case .green: print("Green")
case .blue: print("Blue")
}
if case Matches a specific case and extracts associated value
if case .someValue(let value) = optionalEnum {
    print(value)
}
guard case Exits early if a case does not match
guard case .success(let data) = result else {
    return
}

Example: Switch with Associated Values

enum NetworkError: Error {
    case timeout(seconds: Int)
    case invalidURL(String)
    case serverError(Int)
}

func handleNetworkError(_ error: NetworkError) {
    switch error {
    case .timeout(let seconds):
        print("Request timed out after \(seconds) seconds")
    case .invalidURL(let url):
        print("Invalid URL: \(url)")
    case .serverError(let code):
        print("Server error with code: \(code)")
    }
}

Example: Using if case for Optional Enum

enum Result<T> {
    case success(T)
    case failure(String)
}

let optionalResult: Result<String> = .success("Hello")

if case .success(let value) = optionalResult {
    print(value) // Prints: Hello
}
Note: The switch statement must be exhaustive when matching all cases of an enum, or use _ to handle remaining cases.
Warning: When using if case, ensure that the pattern matches the actual enum case to avoid runtime errors.

5. Using Recursive Enumerations

Recursive enumerations in Swift allow an enumeration to have cases that refer to the enumeration type itself. This is particularly useful for modeling hierarchical or tree-like structures.

Case Description Usage Example
leaf Represents a terminal node in the tree structure. .leaf(42)
node Represents a node that contains other nodes or leaves. .node(.leaf(1), .leaf(2))

Example: Defining a Recursive Enum

enum Tree<T> {
    case leaf(T)
    case node(Tree<T>, Tree<T>)
}

Example: Using Recursive Enum

let tree = Tree.node(.leaf(1), .leaf(2))
print(tree) // Output: node(leaf(1), leaf(2))
Note: Recursive enums must be declared with a generic type parameter to allow self-referencing.

6. Iterating Enumeration Cases

Case Description Usage
allCases Provides a collection of all cases in an enumeration.
enum Color: String, CaseIterable {
    case red, green, blue
}

for color in Color.allCases {
    print(color.rawValue)
}
CaseIterable Protocol that allows enumeration to be iterated over all its cases.
enum Direction: CaseIterable {
    case north, south, east, west
}

let directions = Direction.allCases
print(directions.count) // 4

7. Adding Computed Properties

Property Type Description
rawValue String Computed property that returns the raw value of the enum case.
isAvailable Bool Computed property indicating if the enum case is available in current context.

Example: Using Computed Properties in an Enum

enum NetworkStatus {
    case online
    case offline
    case connecting
    
    var rawValue: String {
        switch self {
        case .online: return "Online"
        case .offline: return "Offline"
        case .connecting: return "Connecting"
        }
    }
    
    var isAvailable: Bool {
        switch self {
        case .online, .connecting: return true
        case .offline: return false
        }
    }
}

let status = NetworkStatus.online
print(status.rawValue) // "Online"
print(status.isAvailable) // true

8. Adding Methods to Enumerations

In Swift, you can add methods to enumerations using the static keyword for type-level methods or self for instance methods. These methods can access the enumeration's cases and associated values.

Method Type Description Example
Instance Method Operates on a specific instance of the enum.
enum Color {
    case red, green, blue
    
    func description() -> String {
        switch self {
        case .red: return "Red"
        case .green: return "Green"
        case .blue: return "Blue"
        }
    }
}
let color = Color.red
print(color.description()) // Output: Red
Static Method Operates on the enum type itself, not an instance.
enum Direction {
    case north, south, east, west
    
    static func allDirections() -> [Direction] {
        return [.north, .south, .east, .west]
    }
}
let allDirections = Direction.allDirections()
print(allDirections.count) // Output: 4

Example: Using Associated Values with Methods

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
    
    func area() -> Double {
        switch self {
        case .circle(let radius):
            return Double.pi * radius * radius
        case .rectangle(let width, let height):
            return width * height
        }
    }
}

let circle = Shape.circle(radius: 5.0)
let rectangle = Shape.rectangle(width: 4.0, height: 6.0)

print(circle.area()) // Output: 78.53981633974483
print(rectangle.area()) // Output: 24.0

9. Using Enum Initializers

Initializer Description Example
init(rawValue: String) Initializes an enum with a raw value of type String
enum Color: String {
    case red = "red"
    case green = "green"
    case blue = "blue"
}

let color = Color(rawValue: "red") // Optional(Color.red)
init?(rawValue: RawValue) Initializes an enum with a raw value, returning an optional
enum Status: String {
    case active = "active"
    case inactive = "inactive"
}

let status = Status(rawValue: "pending") // nil
init?(rawValue: Int) Initializes an enum with a raw value of type Int
enum Direction: Int {
    case north = 0
    case east = 1
    case south = 2
    case west = 3
}

let direction = Direction(rawValue: 1) // Optional(Direction.east)
Note: Raw values must be unique within the enum.

10. Implementing Custom String Convertible

Protocol Description Usage
CustomStringConvertible Defines a custom string representation for a type. Used to provide a human-readable description of an instance.

Example: Implementing CustomStringConvertible

enum Color: String, CustomStringConvertible {
    case red = "red"
    case green = "green"
    case blue = "blue"

    var description: String {
        return self.rawValue.capitalized
    }
}

let color = Color.red
print(color) // Output: Red

Was this article helpful?