How to determine Error response from Swift.Result and Enum

I created a method that uses Swift.Result to return an image and an Error from a URLSession. I also created an Enum for the errors.

When a Swift.Result error is returned, how can I tell the difference between the 4 enums?

fetchImage(with: url) { (result) in
    switch result {

    case .failure(let err):
        print(err)

        // how can I determine which of the 4 enum errors was returned?

        /* Example
        if failErr { ... }
        if responseStatusCodeErr { ... }
        if dataIsNil { ... }
        if catchErr { ... }
        */

    case .success(let img):
        // ...
    }
}

Enum:

enum SessionDataTaskError: Error {
    
    case failErr(Error)
    case responseStatusCodeErr(Int)
    case dataIsNil
    case catchErr(Error)
}

URLSession:

fetchImage(with url: URL, completion: @escaping (Swift.Result<[UIImage], Error>)->Void) {

    URLSession.shared.dataTask(with: url) { (data, res, error) in

        if let error = error {
            completion(.failure(SessionDataTaskError.failErr(error)))
            return
        }
    
        if let response = res as? HTTPURLResponse {
            guard 200 ..< 300 ~= response.statusCode else {
                completion(.failure(SessionDataTaskError.responseStatusCodeErr(response.statusCode)))
                return
            }
        }
    
        guard let data = data else {
            completion(.failure(SessionDataTaskError.dataIsNil))
            return
        }
    
        do {

            // all good ...

        } catch {

            completion(.failure(SessionDataTaskError.catchErr(error)))
        }
    }.resume()
}

>Solution :

First, I would change your completion handler to only take SessionDataTaskError as the error type of the Result, because you never call it with any other types of Error:

func fetchImage(with url: URL, 
    completion: @escaping (Swift.Result<[UIImage], SessionDataTaskError>)->Void) {

Then, you can use pattern matching to match the 4 cases of errors:

fetchImage(with: url) { (result) in
    switch result {
    case .failure(let error):
        print(error)
        switch error {
        case .failErr:
            // ...
        case .responseStatusCodeErr:
            // ...
        case .dataIsNil:
            // ...
        case .catchErr:
            // ...
        }
    case .success(let img):
        // ...
    }
}

If in every case you do different things, you can avoid the nested switch by doing:

fetchImage(with: url) { (result) in
    switch result {

    case .failure(.failErr):
        // ...
    case .failure(.responseStatusCodeErr):
        // ...
    case .failure(.dataIsNil):
        // ...
    case .failure(.catchErr):
        // ...
    case .success(let img):
        // ...
    }
}

If there are different types of errors that fetchImage can produce and you just want to check for the four SessionDataTaskErrors, you can use a type pattern let error as SessionDataTaskError, and add an extra case to handle other kinds of errors.

fetchImage(with: url) { (result) in
    switch result {
    case .failure(let error as SessionDataTaskError):
        print(error)
        switch error {
        case .failErr:
            // ...
        case .responseStatusCodeErr:
            // ...
        case .dataIsNil:
            // ...
        case .catchErr:
            // ...
        }
    case .failure(let otherError):
        print("Other Error:", otherError)
    case .success(let img):
        // ...
    }
}

Leave a Reply