Skip to Content
Course content

20: Enums with Associated Values

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen a lot of developers start their network layers like this. It seems intuitive at first: you define an enum for the state and a few optional variables to hold the actual results. Take a look at this snippet:

enum RequestStatus {
    case loading
    case success
    case failure
}

var status: RequestStatus = .loading
var responseData: String?
var errorMessage: String?

// Simulating a successful network call
status = .success
responseData = "Welcome to the dashboard!"

The "Impossible State" Trap

On the surface, this works. But here is where you'll run into trouble as your app grows. Because the status and the responseData are separate variables, they can get out of sync. What happens if a junior dev on your team sets the status to .success but forgets to assign the responseData? Or worse, what if the status is .failure, but responseData still holds the value from a previous successful request?

You end up with "impossible states"—scenarios where your logic says the request failed, but your data variable says it succeeded. You'll find yourself writing defensive code everywhere: if status == .success && responseData != nil { ... }. It's tedious and prone to bugs.

Binding Data Directly to the Case

This is exactly why Swift gives us associated values. Instead of having a separate variable for the data, we can attach that data directly to the enum case itself. This makes the data dependent on the state.

enum RequestStatus {
    case loading
    case success(String) // The data is now part of the case
    case failure(String) // The error message is part of the case
}

// Now, it's physically impossible to have a .success state without the string.
let status = RequestStatus.success("Welcome to the dashboard!")

I love this pattern because it turns a runtime possibility (a null pointer or missing data) into a compile-time guarantee. If you are in the .success state, you must have the associated string. Period.

Extracting Values with Pattern Matching

Since the data is now "wrapped" inside the enum case, you can't just access it with a dot. You have to extract it. The most common way is using a switch statement, which I highly recommend because it forces you to handle every single possibility.

let currentStatus = RequestStatus.failure("404 Not Found")

switch currentStatus {
case .loading:
    print("Showing spinner...")
case .success(let message):
    print("Success: \(message)")
case .failure(let error):
    print("Error: \(error)")
}

Sometimes, you don't care about the other states and just want to do something if the request succeeded. In those cases, don't write a whole switch block. Use if case let. It's a shorthand that keeps your code clean:

if case let .success(message) = currentStatus {
    print("Quick win: \(message)")
}

Basically, you're telling Swift: "If the status matches the .success pattern, grab the associated value and call it 'message', then run this block." It's a powerful way to drill down into your data without the boilerplate of a full switch.




📋 Practical Task

Building a Multi-Stage User Authentication State Machine

You are building a login flow. The user goes through several stages, and each stage carries different information. Your goal is to replace a clunky set of optional variables with a single Enum using associated values.

Requirements:

  • Create an enum called AuthStatus.
  • Define a loggedOut case (no associated value).
  • Define a verifying case that takes an email (String).
  • Define a loggedIn case that takes a username (String) and a userId (Int).
  • Define an error case that takes an errorCode (Int).

The Challenge:

Write a function handleAuthChange(status: AuthStatus) that uses a switch statement to print a specific message for each state. For the loggedIn state, print: "Welcome back [username] (ID: [userId])". For the verifying state, print: "Verification email sent to [email]".

Rating
0 0

There are no comments for now.

to be the first to leave a comment.