Result type
Purpose
The response from a network call provides us with data or error.
We recover fruits with the following model :
struct FruitsResponse: Decodable {
let results: [Fruit]
}
struct Fruit: Decodable, Equatable, Identifiable {
let id: Int
let name: String
let overview: String
}
In a network call with a classic completion like :
func getFruits(_ completion: @escaping (FruitsResponse?, Error?) -> Void) {}
there are 4 possibilities:
- completion(fruitsResponse, nil)
- completion(nil, error)
- completion(fruitsResponse, error)
- completion(nil, nil)
completion(nil, nil) or completion(fruitsResponse, error) doesn't make sense because we expect either a response with correct data or an error.
Thus we can use the Result type:
func getFruits(_ completion: @escaping (Result<FruitsResponse, Error>) -> Void) {}
The Result type is an enum with two cases:
- success (completion(.success(decoded)))
- failure (completion(.failure(error!)))
we have either a success or a failure, not both.
Example
For a classic call :
func getFruits(_ completion: @escaping (FruitsResponse?, Error?) -> Void) {
let url : URL(string: "https://-------")
URLSession.shared.dataTask(with: url) { data, _, error in
guard error == nil else {
completion(nil, error)
return
}
do {
let decoded = try jsonDecoder.decode(FruitsResponse.self, from: data!)
completion(decoded, nil)
} catch {
completion(nil, error)
}
}.resume()
}
The response is retrieved at the controller :
getFruits { [weak self] fruitsResponse, _ in
guard let fruitsResponse = fruitsResponse else { return }
DispatchQueue.main.async {
self.data = fruitsResponse.results
}
using Result, we obtain :
func getFruits(_ completion: @escaping (Result<FruitsResponse, Error>) -> Void) {
let url : URL(string: " ")
URLSession.shared.dataTask(with: url) { data, _, error in
guard error == nil else {
completion(.failure(error!))
return
}
do {
let decoded = try jsonDecoder.decode(FruitsResponse.self, from: data!)
completion(.success(decoded))
} catch {
completion(.failure(error))
}
}.resume()
}
The call in the controller becomes :
getFruits { [weak self] result in
switch result {
case let .success(fruitsResponse):
DispatchQueue.main.async {
self?.movies = fruitsResponse.results
}
case .failure:
break
}
}
We are obliged to treat both cases of the enum.
Thanks for reading and have fun coding