Шаблон Swift Catch, который связывает ошибку с переменной

Использование Swift 4.2 и XCode 10

В Swift 4.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 {

но компилятор, похоже, не автоматически привязывает переменную «ошибка», и я не вижу такой опции, как

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

Если я просто перехватываю все ошибки, я могу легко получить переключатель в центральном методе, который соответствующим образом разделяет различные случаи ошибок декодирования. Но я хочу иметь возможность избежать отправки ошибок, не связанных с декодированием, в этот коммутатор. Я также могу разделить свои блоки do{}, чтобы выполнять в них только шаги декодирования, но это также делает код беспорядочным, особенно если вы декодируете несколько сообщений, чередующихся с другими действиями.

Предложения? Спасибо всем!


person escapedcanadian    schedule 02.01.2019    source источник


Ответы (2)


Синтаксис, используемый в строке catch, точно такой же синтаксис шаблона, который используется в case строки switch. Если вы знаете, как написать case, вы знаете, как написать catch.

Так, например, вы жалуетесь:

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

Верно. Но это действительно:

} catch let decodingError as DecodingError { 

О, какая разница в одной букве.

person matt    schedule 02.01.2019
comment
Спасибо, Мэтт. К сожалению, я думаю, что ваше решение только показывает еще одну мою ошибку/предположение. Когда я использую as, компилятор теперь жалуется, что мой блок catch не является исчерпывающим. Это не позволяет DecodingError заменять все его перечисления. :( - person escapedcanadian; 02.01.2019
comment
Он обозначает все случаи перечисления; проблема в том, что вы можете выдать какую-то другую ошибку. Как и в случае с переключателем, случаи должны быть исчерпывающими. Вот почему вам по-прежнему нужны catch или catch let nonDecodingError в конце вашей серии уловов. В конце всегда нужен зачистной улов! Однако это не имеет значения; Я ответил на вопрос, который вы задали. - person matt; 02.01.2019
comment
Конечно, вы сделали, и очень ценю. Я запутался, когда тестировал ваше решение, попробовав его в методе, который не объявлял броски вверху. В других моих случаях это было, поэтому блок catch не должен был быть исчерпывающим. Спасибо еще раз. - person escapedcanadian; 02.01.2019
comment
Правило состоит в том, что конструкция do...catch обычно должна быть исчерпывающей, т. е. ей нужна подчистка catch в конце. Только внутри функции throws он не должен быть исчерпывающим, потому что ошибка, выданная в блоке do, но не обнаруженная ни одним блоком catch, может выйти из функции и быть перехваченной выше по цепочке вызовов. - person matt; 02.01.2019

Это все еще намного больше кода, чем хотелось бы, но, возможно, немного чище:

} 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)
}
person Juan de la Torre    schedule 08.01.2021