1

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

Community
  • 1
  • 1
chuky
  • 1,037
  • 1
  • 12
  • 21

3 Answers3

1

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)
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • The documentation part still doesn't really describe multi-catch, as a catch clause without pattern will catch all errors, not just the ones I want to specify (my understanding is that 'let error as HTTPError' is a pattern already). The switch-statement in your answer looks promising though, I will try to implement that while using the message I receive in the error. – chuky Feb 14 '16 at 21:26
  • I've implemented your switch-example and it works quite well, the only issue I see is that 'message' is not the String-message, but the complete enum-value, so the print(message) will result in something like BAD_REQUEST("Field X missing"). Do you have any proposal how to overcome this? – chuky Feb 15 '16 at 20:55
  • I see and added a suggestion – vadian Feb 15 '16 at 21:27
  • Your edit is working perfectly. I've tried it before with a property in the enum, but couldn't get it to work with the message, although it was so simple... Thank you very much, I accepted your answer :) – chuky Feb 16 '16 at 17:29
0
enum Error: ErrorType {
    case E1
    case E2
    case E3
}

func foo(e: Error) throws {
    throw e
}

do {
    try foo(Error.E2)
} catch Error.E1 {
    print(Error.E1) // only Error.E1
} catch {
    print("error: ", error) // all other errors
}
/*
error:  E2
*/
user3441734
  • 16,722
  • 2
  • 40
  • 59
  • 1
    That is not really multi-catch, the second catch will pick all other errors, not just the ones I want to specify. – chuky Feb 14 '16 at 21:20
  • the second catch all errors except Error.E1. you wrote 'I want them all to do the same (except for the NOT_FOUND)' – user3441734 Feb 14 '16 at 21:25
  • Yeah, the ones I know of. But it might be possible that the method throws other errors I'm not aware about. – chuky Feb 14 '16 at 21:28
  • you not aware about? in that case your code will crash .... all thrown errors must be catch somewhere in you code – user3441734 Feb 14 '16 at 21:32
  • @user3441734 no, not all thrown errors have to be catched - the ones you will not catch will rightfully crash the program. if the program cannot recover from an error a crash is the appropriate responce. – luk2302 Feb 14 '16 at 21:34
  • @luk2302 correct, although I would catch all errors in a general catch-block like "unknown error". – chuky Feb 14 '16 at 21:35
  • @luk2302 i wrote the same. if thrown error is not catch somewhere, the execution will crash – user3441734 Feb 14 '16 at 21:36
  • yes, but your following assumption "all thrown errors must be catch somewhere in you code" is wrong. Especially in Swift you do not *have to* catch all thrown errors - you can simply propagate them up if you are unaware of them or choose to ignore them. – luk2302 Feb 14 '16 at 21:38
  • @luk2302 try to make 'command line' project with code from my example and remove the second catch statement and run it. I am sure, that it will crash with 'fatal error' with something like 'error raised at top level'. all thrown error must be catch-ed somewhere – user3441734 Feb 14 '16 at 21:48
  • You are not getting my point - yes, the program will crash. But that is okay. If you as the developer *decide* E2 cannot and will not be thrown or you simply do not know how to react to it, you can choose to ignore it. It will still result in a perfectly fine program - that will crash. That is why there even is the `try!` operator: the coder can choose to assume the error is not thrown - if that still happens, then a crash is the one and only appropriate response. – luk2302 Feb 14 '16 at 21:52
  • @luk2302 I agree :) So, that should be considered as a bug in the code. I don't think that is a wonted feature of OP – user3441734 Feb 14 '16 at 21:57
0

The multi-pattern catch is now supported in Swift 5.3 Check the proposal details: https://github.com/apple/swift-evolution/blob/master/proposals/0276-multi-pattern-catch-clauses.md

RTXGamer
  • 3,215
  • 6
  • 20
  • 29