В цепочке Result нужно восстановиться только после ошибки, превратив её в успешное значение. Какой механизм Swift позволяет сделать это без перехвата ошибки?
Для этого используется метод flatMapError. Он получает ошибку из failure и должен вернуть новый Result, поэтому обработчик может либо восстановить операцию через success, либо вернуть другую ошибку через failure.
Result отделяет вычисление от его последующей обработки: операция сразу возвращает значение успеха или ошибки. Функциональные преобразования позволяют строить цепочки таких операций без немедленного перехода к do-catch.
flatMapError решает обратную задачу по отношению к flatMap: он раскрывает ветку ошибки и позволяет продолжить вычисление после неё, сохраняя типобезопасность результата.
Предположим, основной источник данных недоступен, но приложение может безопасно использовать кэш. Простое преобразование ошибки в другую ошибку недостаточно: требуется заменить failure на полноценный success с кэшированным значением.
Метод mapError для этого не подходит. Он изменяет только тип или содержимое ошибки и всегда оставляет результат в состоянии failure.
flatMapError вызывается только для результата с ошибкой. Замыкание получает исходную ошибку и возвращает Result с тем же успешным типом, но потенциально другим типом ошибки.
В примере recovered содержит success, потому что обработчик преобразовал конкретную ошибку в резервное значение. Если бы обработчик вернул .failure(...), результат остался бы ошибочным.
Для преобразования ошибки без возможности восстановления используется mapError. Для преобразования успешного значения с сохранением возможной ошибки применяется map, а для запуска следующей операции, которая сама возвращает Result, — flatMap.
Компромисс заключается в том, что восстановление должно быть семантически безопасным. Нельзя превращать любую ошибку в значение по умолчанию, если это скрывает потерю данных или нарушает требования бизнес-логики.
Сервис сначала запрашивает профиль с сервера, а при временной недоступности может использовать локальный снимок. Возможны три подхода.
Перехватить ошибку через do-catch можно, но тогда цепочка преобразований прерывается и логика восстановления смешивается с императивным управлением потоком. Использовать mapError проще, однако он не позволяет вернуть профиль: ошибка лишь изменит форму.
Выбран flatMapError: ошибка offline преобразуется в success с локальным снимком, а необратимые ошибки возвращаются как failure. В результате контракт остаётся типизированным, резервная стратегия видна в цепочке, а вызывающий код по-прежнему может обработать оставшиеся ошибки.
1. Чем flatMapError отличается от mapError?
mapError принимает ошибку и возвращает новую ошибку. Он не может создать успешный результат, поэтому после его выполнения состояние failure сохраняется.
flatMapError принимает ошибку и возвращает целый Result. Поэтому он может либо восстановить операцию через success, либо передать изменённую ошибку через failure.
2. Вызывается ли обработчик flatMapError для успешного результата?
Нет. Для success обработчик flatMapError не выполняется, а успешное значение передаётся дальше без изменений. Это позволяет безопасно добавлять fallback-логику, не затрагивая уже успешные операции.
3. Можно ли с помощью flatMapError изменить тип ошибки?
Да. Возвращаемый обработчиком Result может иметь другой тип ошибки, если типы согласованы с сигнатурой метода. Однако при восстановлении в success нужно учитывать, что новый тип ошибки всё равно должен корректно описывать возможный нерешённый сценарий.