Search code examples
xcode10swift4.2catch-block

Swift Catch Pattern that binds the error to a variable


Using Swift 4.2 and XCode 10

In Swift 4.2, DecodingError is an enumeration. There are (currently) four different cases. I can catch each case separately, and bind variables that I can use to log the error as in the following code ...

do {
    let model = try jsonDecoder.decode(BattleShip.self, from: jsonData!)
    print(model)
} catch DecodingError.dataCorrupted(let context) {
    print(context.debugDescription)
} catch DecodingError.keyNotFound(let key, let context) {
    print("\(key.stringValue) was not found, \(context.debugDescription)")
} catch DecodingError.typeMismatch(let type, let context) {
    print("\(type) was expected, \(context.debugDescription)")
} catch DecodingError.valueNotFound(let type, let context) {
    print("no value was found for \(type), \(context.debugDescription)")
} catch {
    print("I know not this error")
}

But this is a lot of code to put everywhere I could encounter a decoding error. And, if my do{} block has multiple calls that throw, I may need to handle the errors that those methods call differently. The pattern I am trying to implement looks like this ... where decodingError(error) has all of the messy code above

do {
    let object1 = try decoder.decode(SomeClass.self, from: someData)
    try object2.methodThatThrowsSomeOtherError()
} catch <all decoding errors> {      // this is invalid pseudocode
    MyCentralLogger.log.decodingError(error)
} catch let nonDecodingError {
    MyCentralLogger.log.error(nonDecodingError)
}

I can have a catch pattern like this that seems to satisfy all of the enumeration cases (at least it compiles)

} catch is DecodingError {

but the compiler doesn't seem to autobind the 'error' variable, and I don't see any option like

} catch let decodingError is DecodingError {  // THIS IS NOT VALID

If I just catch all errors, I can easily have a switch in a central method that separates the different decoding error cases appropriately. But I want to be able to avoid sending non-decoding errors into that switch. I can also separate my do{} blocks so that I'm only performing decoding steps in it, but this also makes code messy, particularly if you are decoding multiple messages interspersed with other actions.

Suggestions? Thanks all!


Solution

  • The syntax used in a catch line is exactly the same pattern syntax used in the case of a switch. If you know how to write a case you know how to write a catch.

    So, for example, you complain:

    } catch let decodingError is DecodingError {  // THIS IS NOT VALID
    

    Right. But this is valid:

    } catch let decodingError as DecodingError { 
    

    Oh what a difference one letter makes.