Below is some example code that covers common patterns for catching specific types of errors. This code is valid for Swift 5.1. I figure this might be useful for some folks since the official documentation doesn’t provide examples for all of these, and few tutorials cover it completely.

do {
    try callThatCanThrow()
} catch DecodingError.keyNotFound {
    // for error enums, this will match a specific case
} catch DecodingError.keyNotFound(let key, let c) where key.stringValue == "test" {
    // for error enums, this will match a specific case, unwrap the payload,
    // and execute this block if the where condition is met
} catch DecodingError.keyNotFound(let key, let c) {
    // for error enums, this will match a specific case
    // and unwrap the payload
} catch let DecodingError.keyNotFound(key, c) {
    // for error enums, this will match a specific case
    // and unwrap the payload (alternative syntax)
} catch is DecodingError {
    // catch all errors of type DecodingError, but don't bind it to a variable
} catch let error as DecodingError {
    // catch all errors of type DecodingError, and bind it to `error`
} catch let error as NSError {
    // catch all errors, but convert covert to NSError before binding
    // AFAIK, all errors can be implictly converted to NSError,
    // so this should always succeed
} catch {
    // catch everything else, and implicitly bind it to a variable named `error`
}