Search code examples
swift2try-catchmulti-catch

Swift 2 - try multi-catch


I have searched around for a bit and couldn't find anything - not even a question on this site - so now I wonder whether Swift 2 supports multi-catch blocks like Java does.

Java-Example:

try {
    someMethod(); // throws ExceptionOne, ExceptionTwo
} catch(ExceptionOne | ExceptionTwo e) {
    handleMyException();
} catch(Exception e2) {
    handleOtherExceptions();
}

In Swift, I wasn't able to implement a similar solution. This question explains a nice way to handle different errors of one ErrorType enum, but my requirements seem to be different as my error is initialized with a message coming from an HTTP-response. This is my enum:

enum HTTPError: ErrorType {
    case BAD_REQUEST(message: String)
    case UNAUTHORIZED(message: String)
    case NOT_FOUND
    case INTERNAL_SERVER_ERROR(message: String)
    case NOT_IMPLEMENTED(message: String)

    static func getErrorForStatusCode(statusCode: Int, message: String? = nil) -> HTTPError {
        switch statusCode {
        case 400:
            return HTTPError.BAD_REQUEST(message: message!)
        case 401:
            return HTTPError.UNAUTHORIZED(message: message!)
        case 404:
            return HTTPError.NOT_FOUND
        case 500:
            return HTTPError.INTERNAL_SERVER_ERROR(message: message!)
        case 501:
            return HTTPError.NOT_IMPLEMENTED(message: message!)
        default:
            return HTTPError.INTERNAL_SERVER_ERROR(message: message!)
        }
    }
}

When I want to throw an error of that enum, I use such code:

func someMethod() throws {
    ...
    HTTPError.getErrorForStatusCode(400, message: "Field X missing") // Actual values coming from HTTP-response
    ...
}

Now, in the calling method, I have to catch every error of the enum separately, although I want them all to do the same (except for the NOT_FOUND, but that doesn't matter here):

try {
    someMethod() // throws
} catch HTTPError.BAD_REQUEST(let message) {
    // display 'message' to user
} catch HTTPError.UNAUTHORIZED(let message) {
    // display 'message' to user
} catch HTTPError.INTERNAL_SERVER_ERROR(let message) {
    // display 'message' to user
}
...

Does Swift 2 really not contain multi-catch? Or is there another solution that would help me solve my problem?

EDIT: It seems my desired behavior isn't quite clear: I want to handle the errors of type HTTPError (except HTTPError.NOT_FOUND) in the same way, that's why I'm asking for multi-catch.

I will still implement a general catch-block to cover all errors I'm not aware about (e.g. timeout, nil-access, ...), but those errors that come with a backend-message should be treated specially.

Thanks, chuky


Solution

  • Swift 2 does support multi-catch

    From the documentation

    You write a pattern after catch to indicate what errors that clause can handle. If a catch clause doesn’t have a pattern, the clause matches any error and binds the error to a local constant named error.

    So get that error constant and use a switch statement.

    do {
      try someMethod() // throws
    } catch let error as HTTPError {
      switch error  {
      case .NOT_FOUND:
        print("not found")
      case let message: // handle all errors with a message
        print(message)
      }
    }
    

    or with catch pattern

    do {
      try someMethod() // throws
    } catch HTTPError.NOT_FOUND {
        print("not found")
    } catch let message {
        print(message)
    }
    

    which is also exhaustive because all other cases have a message.

    Apart from that you – the developer – are supposed to know exactly the number of cases in the enum so you can handle them reliably.

    Edit:

    Another solution is to add a associatedValue property inside the enum

      var associatedValue : String {
        switch self {
        case BAD_REQUEST(let value) : return value
        case UNAUTHORIZED(let value) : return value
        case INTERNAL_SERVER_ERROR(let value) : return value
        case NOT_IMPLEMENTED(let value) : return value
        default: return ""
        }
      }
    

    and use this switch statement

    do {
      try someMethod() // throws
    } catch HTTPError.NOT_FOUND {
      print("not found")
    } catch let error as HTTPError {
      print(error.associatedValue)
    }