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

Imagine you're ordering a pizza through an app. When you hit that "Order" button, you aren't getting the pizza immediately. Instead, you're entering a state of waiting for an outcome. That outcome is binary: either the delivery driver shows up at your door with a hot pepperoni pizza (Success), or you get a notification saying the shop is out of dough or your address was wrong (Failure).

Crucially, you can't have both. You don't get a half-delivered pizza and a "store closed" notification at the same time. It's one or the other.

In Swift, the Result type is exactly that: a way to wrap the outcome of an operation so that it's explicitly either a success with a value, or a failure with an error. It maps perfectly to our pizza analogy:

  • The Result type: The overall status of your order.
  • .success(Value): The pizza arriving (the value is the actual pizza).
  • .failure(Error): The notification explaining why you're still hungry (the value is the specific error).

Stop juggling double optionals

Before Result became the standard, we used to handle asynchronous callbacks by passing two optional arguments: (Data?, Error?). I've spent way too many hours debugging code where, due to a bug, both were nil or, somehow, both were populated. It was a mess because the compiler couldn't force you to handle the "impossible" states.

Result fixes this by using an enum. Since it's an enum, it's mathematically impossible to be in both states at once. Here is how I typically implement a network fetch using this pattern:

enum NetworkError: Error {
    case badURL
    case serverError
    case decodingFailed
}

struct UserProfile {
    let username: String
    let bio: String
}

func fetchUserProfile(userId: String, completion: @escaping (Result<UserProfile, NetworkError>) -> Void) {
    // Simulating a network call
    let success = true 
    
    if success {
        let user = UserProfile(username: "swift_dev", bio: "Coding the future.")
        completion(.success(user))
    } else {
        completion(.failure(.serverError))
    }
}

Handling the outcome with precision

The beauty of Result is that it forces you to acknowledge the failure path. I always use a switch statement to handle the response. It keeps the "happy path" and the "error path" visually separated, which makes the code much easier to scan during a code review.

fetchUserProfile(userId: "123") { result in
    switch result {
    case .success(let profile):
        print("Welcome back, \(profile.username)!")
    case .failure(let error):
        switch error {
        case .badURL:
            print("Check your connection.")
        case .serverError:
            print("Our servers are acting up. Try again later.")
        case .decodingFailed:
            print("We got the data, but it looks weird.")
        }
    }
}

One little pro-tip: if you only care about the success value and want to ignore the error (though I'd advise against this in production), you can use try? result.get(). The get() method is a handy built-in that throws the error if the result is .failure or returns the value if it's .success.




📋 Practical Task

Implementing a Secure Vault Access Simulator

You need to build a system that simulates accessing a secure digital vault. The vault should only open if the provided passcode is correct. If the passcode is wrong, or if the vault is "locked for maintenance," it should return a specific error.

Your Task:

  1. Create an enum called VaultError that conforms to Error with two cases: invalidPasscode and maintenanceMode.
  2. Create a function called openVault(passcode: String) that returns a Result<String, VaultError>.
    • If the passcode is "1234", return .success with the string "Secret Documents Accessed".
    • If the passcode is "0000", return .failure(.maintenanceMode).
    • For any other passcode, return .failure(.invalidPasscode).
  3. Call this function three times with different passcodes and use a switch statement to print a user-friendly message for every possible outcome (including the specific errors).
Rating
0 0

There are no comments for now.

to be the first to leave a comment.