How to use Result in Swift?

Since Result has been introduced in Swift standard library, the way we normally passed errors has changed, especially while calling asynchronous APIs the completion handlers are more readable.

With Result it’s easy to handle the value, as the value is binary, just like using a boolean value.

Let’s dive in with examples, and see how to use Result in our code?

Let’s Start

//  Declaration in Swift Library
@frozen enum Result<Success, Failure> where Failure : Error

Result is very easy to use, and it is having two cases for our use, Success and Failure, both of them can hold values using generics, which means you can choose the type of values, but the value of Failure must conform to Error type. Generics makes our code powerful if used in the right way.

Let’s take an example.

enum APICallError: Error {
    case error(title: String, message: String)
}

Above is the Error type I use

Whenever my API gives an error, I need to show an alert to the user with title and message. And if all goes well, I’ll return a Dictionary received from the API. Let’s see how we can do this.

// Result<[String: Any], APICallError>
func getListForUser(_ user: String, completion: @escaping (Result<[String: Any], APICallError>) -> ())

Here, in the Result, the first type [String:Any] is used in case the API was successful, and the second type APICallError is used in case there was a failure. How do we use them?

func getListForUser(_ user: String, completion: @escaping (Result<[String: Any], APICallError>) -> ()) {
    
    // Calling API and getting the result...
    
    
    // In case of Success
    completion(.success(jsonDict))

    // In case of Failure
    completion(.failure(APICallError.error(title: "Network Error", message: "Something went wrong, Please try again later")))
}

It is as easy as defined above in the code. If there’s a Success, simply use .success(), and if there’s a Failure, use .failure().

How do we handle the Result?

self.getListForUser(user) { result in
	// Use Switch
    switch result {
    case .success(let dictionary):
        // Do some work
    case .failure(let error):
        switch error {
        case let .error(title: title, message: message):
            // Show Alert
        }
    }
}

Using switch is one of the most common ways of handling the Result, code is more readable and structured here.

Note: You can add more cases to APICallError, and handle them inside the other switch declared in the failure case.

There are few more way of handling the Result.

// E.g. 1
if case .success(let dictionary) = result {
    // Use Dictionary
}

if case .error(let error) = result {
	// Handle error
}


// E.g. 2
do {
    if let dictionary = try result.get() {
        // Use Dictionary
    }
}catch {
    // Handle error
}

Use if case .success(let dictionary) = result to retrieve the dictionary from the result without handling the error, or add another if statement to handle the error.

Or try the old way of handling the errors by using Do-Catch with the help of get() function provided in Result, which gives the success value if available or throws an error.

Result is now widely used by many third-party SDKs. Start using Result, it helps you improve your code readability and structure.

I hope you guys enjoyed reading my article. There’s a lot more to learn together, so subscribe to stay updated about my upcoming articles.

If you have any suggestions or questions, Feel free to connect me on Twitter or Reddit. 😉

Generics Introduction in Swift

How to create Custom Animations in SwiftUI?