Search code examples
iosswiftenumsnsurlsession

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):
            // ...
        }
    }