0

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()
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • 2
    Since you seem to always return a `SessionDataTaskError`, it should be `Swift.Result<[UIImage], SessionDataTaskError >` instead of `Swift.Result<[UIImage], Error>`, then, just do a `switch` on `error`. See https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html#ID148 – Larme Jun 12 '22 at 17:12
  • @Larme ahhh, that makes sense. let me try it out now – Lance Samaria Jun 12 '22 at 17:13

1 Answers1

2

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):
        // ...
    }
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Thanks, everything works. Small issue though, in your last scenario `case .failure(let error as SessionDataTaskError)`, I got a warning `'as' test is always true`. Did the warning not appear for you? – Lance Samaria Jun 12 '22 at 17:28
  • @LanceSamaria Maybe I wasn't very clear, but that is what you should use when `fetchImage` can throw different kinds of errors, not just `SessionDataTaskError`, in which case the error type of `Result` must remain as `Error`, and not `SessionDataTaskError`. – Sweeper Jun 12 '22 at 17:32