A more readable Result

Jeroen de Vrind
Feb 24, 2019 · 3 min read
Photo by Dmitry Ratushny on Unsplash

When making a network call we need a callback, or so called completion handler, that tells us if our call was successful and returns some data or if something has failed.

The traditional Objective-C approach of completion handlers has it’s shortcomings: it is not clear what you can get back. Data, an error, both or neither of them.

func load(completion: @escaping (Data?, Error?) -> Void) {
....
}

The dataTask() method from URLSession is even worse: it calls its completion handler with (Data?, URLResponse?, Error?). So it has eight possible outcomes.

In Swift we can do better. We have generics and enums with associated values. We can create a Result type implemented as an enum with two cases: success and failure.

enum Result<Success, Failure: Error> {
case success(Success)
case failure(Failure)
}

They can contain any associated value as a success value, but failure must conform to Swift’s error type. This new type has a few benefits. First of all it makes our code a lot more readable. Our method now becomes like this:

func load(completion: @escaping (Result<Data, LoadingError>) -> Void) {
...
completion(.success(data))
...
completion(.failure(error))
...
}

Or you can make it fancy with a typealias such as:

typealias LoadCompletion<Data, LoadingError> = (Result<Data, LoadingError>) -> Void

and the function signature becomes like:

func load(completion: @escaping LoadCompletion<Data, LoadingError> {
...
}

At the call-site it will look like this:

load { result in
switch result {

case .success(let data):
// Handle loaded data

case .failure(let error):
// Handle error
}
}

The advantages are that the Result type can be reused in many different contexts, while still retaining type safety because of the use of generics.

It’s also clear what we get back: it’s either succesful data or an error. So we have now two possible states instead of four.

The error we get back now is strongly typed. Swift’s other error handling mechanism, throwing functions, uses errors that are unchecked and therefore can throw any type of error. As a result you need to always add a default case. With a Result type we can create exhaustive switch blocks.

Using throws forces often to handle errors immediately rather than store them for later processing. Result stashes a value that we can read when needed.

SE-0235 introduces this Result type into the standard library of Swift 5. If you might already use a Result type in your project like above or from some library, then that type will be used instead of Swift’s own Result type, so you can upgrade without breaking your code.

The Result type in Swift 5 has some nice additional features. It comes with a get() method that returns the successful value if it exists or throws an error otherwise. This makes it possible to convert Result into a throwing call:

load { result in
if let data = try? result.get() {
print(data)
}
}

You can use regular if statements to read the cases of an enum like this:

if case .success(let data) = result {
print "Data: \(data)"
}

Result has an initializer that accepts a throwing closure, for example init(contentsOf url: URL) throws. If the closure returns successfully a value, it is placed in the success case of Result. If it throws an error, it’s placed in the error case:

let result = Result { try String(contentsOfFile: someFile) }

Instead of using a specific error enum, you can also use the general Error protocol in Swift’s 5 Result type like: Result<Data, Error>. Although you lose the safety of typed errors, you can now throw a variety of different error enums.

Furthermore it’s good to know you can use Result also with methods like map(), flatMap(), mapError() and flatMapError(). The map() method, for example, transforms the success value into a different kind of value that you specify in the closure. If there’s a failure instead it just uses that and ignores the transformation.

Btw…is your app already accessible? Legislation is coming up in Europe and your app should meet the Web Content Accessibility Guidelines. You can read all about accessibility in my book: ‘How to develop accessible iOS apps’ available in the Apple Book Store.

Short Swift Stories

Articles about Swift that take less than 10 minutes to…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store