Использование примеров кода, чтобы показать, как бороться с этими неудачными случаями

При начале работы с Combine вы быстро столкнетесь с проблемами обработки ошибок. Каждый поток Combine получает либо значение, либо ошибку, и в отличие от таких фреймворков, как RxSwift, вам необходимо указать ожидаемый тип ошибки.

Чтобы подготовить вас к этим случаям, в этой части я рассмотрю доступные в Combine параметры, позволяющие выявлять, игнорировать и обрабатывать ошибки в потоке. Мы также расскажем о некоторых важных вещах, которые вам нужно знать при возникновении ошибки в вашем потоке.

Только начинаете работать с Combine? Вы можете сначала взглянуть на Начало работы с фреймворком Combine в Swift или мою Игровую площадку Combine.

Объедините потоки и типизированные ошибки

Большое различие между такими фреймворками, как RxSwift и Combine, состоит в том, что в потоках необходимо указывать типизированные определения ошибок. Если мы сравним Observable с его эквивалентом в Combine AnyPublisher, мы увидим разницу в объявлении типа.

public class Observable<Element> : ObservableType
struct AnyPublisher<Output, Failure> where Failure : Error

AnyPublisher требует, чтобы мы указали тип ошибки Failure, в то время как Observable принимает только общий тип Element.

Swift требует от нас думать об обработке ошибок, что мы можем принять за что-то хорошее. Однако это не мешает нам определить ожидаемый тип как просто Swift.Error, что в основном сводится к тому же поведению, что и в RxSwift.

Как только вы потребуете, чтобы ваш поток ожидал ошибки определенного типа, вы столкнетесь с ошибками приведения, поскольку каждый оператор должен возвращать тот же тип ошибки, что и ведущий поток. Давайте углубимся в операторы Combine для обработки ошибок.

Отображение ошибок с помощью mapError

Чтобы сопоставить ошибку с ожидаемым типом ошибки, мы можем использовать оператор mapError. В следующем примере у нас есть сквозная тема, которая ожидает вывода URL и типа ошибки RequestError.

Как только мы начинаем отображать этот поток в URLSessionDataTaskPublisher, мы сразу получаем ошибку, указывающую на несоответствие типа ошибки.

В этом случае решение так же просто, как использование оператора mapError, который преобразует URLError в RequestError, используя случай ошибки сеанса, который мы определили ранее.

Использование оператора повтора

В приведенном выше примере мы использовали URLSessionDataTaskPublisher. Возможно, вы захотите использовать оператор retry перед фактическим принятием ошибки при работе с запросами данных. Требуется определенное количество повторных попыток, прежде чем поток действительно потерпит неудачу.

Выявление ошибок

Если вы хотите выявлять ошибки на раннем этапе и игнорировать их после, вы можете использовать оператор catch. Этот оператор позволяет вам вернуть значение по умолчанию, если запрос не удался. Примеры этого могут быть:

  • Пустой массив для результатов поиска
  • Заполнитель изображения по умолчанию, если запрос изображения не удался

Последнюю мы будем использовать в нашем примере.

Использование replaceError вместо catch

ReplaceError vs Catch: оба оператора кажутся очень похожими. Большая разница в том, что оператор replaceError(:) полностью игнорирует ошибку. Как и в приведенном выше примере, мы не делаем ничего, кроме возврата заполнителя notFoundImage в случае ошибки.

Мы могли бы упростить это, используя оператор replace error, чтобы напрямую отображать любые ошибки в нашем изображении-заполнителе:

Когда оператор assign (to: on :) недоступен

Типичный пример, в котором вам нужно сопоставить ошибки, - это когда вы пытаетесь присвоить исходящее значение свойству объекта. Вы попытаетесь использовать автозаполнение, и обнаружите, что оператор assign(to:on:) недоступен. Следующая ошибка произойдет, если вы будете вынуждены написать код в любом случае:

Для ссылки на метод экземпляра «assign (to: on :)» в «Publisher» типы «RequestError» и «Never» должны быть эквивалентными.

Вы можете исправить это, обнаружив ошибку, как описано в приведенном выше примере, или просто используя оператор assertNoFailure. Этот оператор вызовет фатальную ошибку, поэтому его следует использовать только в том случае, если это ошибка программирования. Если ожидается ошибка, вы всегда должны использовать вместо нее оператор catch.

Заключение

Мы много рассказали об обработке ошибок в Combine, и этого должно быть достаточно, чтобы помочь вам справиться со всеми этими неудачными случаями! Обязательно обрабатывайте ошибки соответствующим образом, а не просто игнорируйте их. Несчастливый поток так же важен для ваших пользователей, как и счастливый поток.

Если вы хотите поэкспериментировать с тем, что вы только что узнали, взгляните на мою Игровую площадку Swift Combine, которая включает страницу об обработке ошибок в Combine.

Чтобы узнать больше о Swift Combine, взгляните на другие мои сообщения в блоге Combine:

Изначально опубликовано на SwiftLee.

Другие сообщения и обновления: @twannl