Swift 将错误绑定到变量的捕获模式

Swift Catch Pattern that binds the error to a variable

使用 Swift 4.2 和 XCode 10

在Swift4.2中,DecodingError是一个枚举。有(目前)四种不同的情况。我可以分别捕获每个案例,并绑定可用于记录错误的变量,如以下代码所示...

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")
}

但是在我可能遇到解码错误的所有地方都放了很多代码。而且,如果我的 do{} 块有多个调用抛出,我可能需要处理这些方法以不同方式调用的错误。我试图实现的模式看起来像这样......其中 decodingError(error) 包含上面所有的混乱代码

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)
}

我可以有一个像这样的 catch 模式似乎可以满足所有枚举情况(至少它可以编译)

} catch is DecodingError {

但是编译器似乎没有自动绑定 'error' 变量,而且我没有看到任何像

这样的选项
} catch let decodingError is DecodingError {  // THIS IS NOT VALID

如果我只捕获所有错误,我可以轻松地在中央方法中进行切换,适当地分隔不同的解码错误情况。但我希望能够避免将非解码错误发送到该交换机。我还可以将我的 do{} 块分开,以便我只在其中执行解码步骤,但这也会使代码变得混乱,特别是如果您正在解码散布在其他操作中的多条消息。

建议?谢谢大家!

catch 行中使用的语法与 switchcase 中使用的模式语法完全相同。如果您知道如何写 case,您就会知道如何写 catch.

因此,例如,您抱怨:

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

没错。但是这个有效的:

} catch let decodingError as DecodingError { 

哦,一个字母有多么不同。

这仍然比预期的代码多很多,但可能更简洁一些:

} catch DecodingError.keyNotFound(_, let context),
        DecodingError.valueNotFound(_, let context),
        DecodingError.typeMismatch(_, let context),
        DecodingError.dataCorrupted(let context) {
    print(context.debugDescription)
    MyCentralLogger.log.decodingError(context.underlyingError)
} catch {
    print(error.localizedDescription)
    MyCentralLogger.log.error(error)
}

结合有效答案和 switch 语句,以下将为您节省一些几乎相同的行:

do {
    let model = try jsonDecoder.decode(BattleShip.self, from: jsonData!)
    print(model)
} catch let decodingError as DecodingError {
    switch decodingError {
    case .typeMismatch(_, let c), .valueNotFound(_, let c), .keyNotFound(_, let c), .dataCorrupted(let c):
        print(c.debugDescription)
    }
} catch {
    print(error.debugDescription)
}

如果,f.i,你的解码数据遗漏了属性 index: Int,它会打印

No value associated with key CodingKeys(stringValue: "index", intValue: nil) ("index").

对于调试目的,这应该足够清楚了。