Swift 5 introduces a new Result type to handle the result of an asynchronous function using an enum.
According to Apple
A value that represents either a success or a failure, including an associated value in each case.
Declaration
1 |
@frozen enum Result<Success, Failure> where Failure : Error |
Before Result type below is the ways in which we create Asynchronous functions:
-
Objective – C style
1 2 3 |
func fetchData(url: URL, completionHandler: @escaping (Data?, Error?) -> Void) { ... } |
The problem is that handling the result of the above function becomes quite tricky. Even if the error argument is nil
, there’s no compile-time guarantee that the data we’re looking for is actually there — it might be nil
as well for all we know, which would put our code in a bit of a strange state.
2. Swift Style
1 2 3 |
func fetchData(url: URL, successHandler: @escaping (Data) -> Void, errorHandler: @escaping (Error?) -> Void) { ... } |
Result Type
They contain only 2 cases that both uses Swift Generic
with associated value:
1. Success
with the value of the result.
2. Failure
with the type that implements Error
protocol.
Implementation
The API which we are using for this is “https://jsonplaceholder.typicode.com/posts”. It is just a dummy API which sends back dummy results. The modal we have created for this demonstration is given below:
1 2 3 4 5 6 7 8 9 |
struct Post: Decodable { let title: String let body: String } enum NetworkError: Error { case domainError case decodingError } |
The Post model is marked with Decodable, which will help us to decode the response and populate our model. The NetworkError enum represents different cases for errors which we might encounter when during our networking adventures. Now that we have our models implemented let’s check out the implementation of fetchPosts function which is responsible for retrieving all the posts from the web API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
func fetchPosts(url: URL, completion: @escaping (Result<[Post],NetworkError>) -> Void) { URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data, error == nil else { if let error = error as NSError?, error.domain == NSURLErrorDomain { completion(.failure(.domainError)) } return } do { let posts = try JSONDecoder().decode([Post].self, from: data) completion(.success(posts)) } catch { completion(.failure(.decodingError)) } }.resume() } |
Into The Standard Library
As part of Swift 5, the standard library is also getting its very own implementation of Result
. One advantage of Result
being included in the standard library is that individual frameworks and apps no longer have to define their own — and more importantly, no longer have to convert between different flavors of the same kind of type.
Swift 5 also brings another interesting change that’s heavily related to Result
(in fact it was implemented as part of the same Swift evolution proposal) — and that’s that the Error
protocol is now self-conforming. That means that Error
can now be used as a generic type that is constrained to having to conform to that same protocol, meaning that the above NSError
-based technique is no longer necessary in Swift 5 — as we can simply use the Error
protocol itself to anonymize errors:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class ImageProcessor { typealias Handler = (Result<UIImage, Error>) -> Void func process(_ image: UIImage, then handler: @escaping Handler) { do { var image = try transformer.transform(image) image = try filter.apply(to: image) handler(.success(image)) } catch { handler(.failure(error)) } } } |
For More information about the Result Type you can check the official Swift Doc by visiting below mention link:
https://developer.apple.com/documentation/swift/result