Продолжаем дополнять серию статей. Сегодня мы разберем как легко обработать ошибку с сервера. То что мы будем сегодня разбирать тесно связано с предыдущими статьями и если вы их ещё не читали то настоятельно рекомендую перейти по ссылкам, прочитать и вернуться. Single Activity с Navigation Component. Или как я мучался с графами. Boilerplate ч. 1 Запросы в сеть с Clean Architecture и MVVM. Boilerplate ч. 2 С этого выпуска мы напишем приложения с такой логикой: Авторизация - реализуем авторизацию, добавим туда обработку ошибок с сервера, сохраним токены и проверку на авторизованность. И при успешной авторизации навигацию на главную страницу. Главная страница - на главной странице у нас будет стягивания листа данных с пагинацией, при нажатии на определенны item с данными нас навигирует в детальную страницу. Детальная страница - реализуем простую детальную страницу куда передается id'шка где мы делаем стягивания по той же id. Объединим то что мы уже ранее разбирали, навигацию и запросы. После начинаем обработку ошибок. Нужно будет создать sealed класс NetworkError в модуле domain: Думая над обработкой ошибок полей куда юзер вводит данные (далее "инпуты") с сервера в приложениях мы с коллегами пришли к оптимальныму решению. Ошибки будут отправляться в виде ключ значение то есть Map. В ключе будет приходить название инпута в значении лист из ошибок. Представим простой кейс, ошибка для инпута email. Ключ придет "email" значение придет . Принимаем лист из ошибок по причине того что ошибок может быть несколько, но вы можете просто этот момент обработать под себя, условно если всегда будет приходить только одна ошибка принимать просто обычную строку то есть String. А ещё мы будем использовать MutableMap позже поймете для чего. Меняем типа обработки  в репозитории слоя domain и обернем наш запрос в Response<T> для того чтобы получать errorBody(). Далее мы рефакторим реализацию мапперов с слоя data в слой domain для того чтобы могли их использовать вместе с generic'ами, далее поймете зачем нам это. Создаем интерфейс DataMapper<T, S> в слое data и дополнительно создаем функции расширения которая поможет нам все реализовать. Имплементируем в SignInResponse И после всего этого меняем реализацию метода doRequest теперь будем называть его doNetworkRequest() потому что будет он работать только с запросами в сеть. И ещё как посоветовали в комментариях с прошлой статьи был убран параметр doSomethingInSuccess. Теперь давайте разберем код. Generic T коим является наша моделька SignInResponse теперь является имплементирующим интерфейс DataMapper<T, S>. А generic S является уже моделькой слоя domain. В параметре request теперь возвращается T с оберткой Response. Далее обычная проверка на isSuccessful и null. При успешной обработке возвращается Either.Right с нашей моделькой SignInResponse который с помощью метода mapToDomain() мапиться в SignIn модельку слоя domain. При ошибке с сервера мы возвращаем Either.Left только уже с NetworkError.Api в котором мы вызываем errorBody() метод и превращаем его в наш MutableMap с помощью функции toApiError(). Давайте посмотрим как теперь выглядит SignInRepositoryImpl. Если что мы на уровне data сохранили токен, уже не обязательно передавать его в слой presentation'a. В UseCase'ах все остается так же поэтому перейдем во ViewModel. Там нужно будет подправить функции collectRequest(). Просто меняем возвращаемый тип с String на NetworkError. Как мы помним из изменений прошлой статьи, у нас появилось две функции collectRequest(), один с маппингом другой без.  Здесь мы поменяли тип возвращения ошибок на NetworkError, но теперь у нас UIState.Error не принимает значение String, значит там тоже меняем. Далее меняем так же в методе collectUIState() который находиться в BaseFragment'e. Далее в обработке нам нужно как-то отобразить ошибку из Map'ы в ошибку внутри инпута. Тот же не правильный username или password, мы вернем ошибку ключ username, а значение лист из строк, в нем может быть ошибки по типу required, incorrect и так далее. Теперь как нам отобразить это, конечно мы можем каждый вручную все прописывать, но лень двигатель прогресса (не пишите повторяющийся код :) ) поэтому мы напишем функцию расширения для этого. Чтобы понимать в какие инпуты что сетить мы будем использовать атрибут tag у вьюшек. Если будет ошибка с ключем username то и у инпута будет tag username. Создаем .kt файл и назовем его NetworkErrorExtensions и пишем там методы обработки. NetworkError.setupApiErrors() - Функция расширяет sealed class NetworkError, принимает в параметр vararg из TextInputLayout и далее проверяет это Api или нет, если да то идет к дальнейшей логике. Пробегается циклом по инпутам которые пришли в параметр, после по их тегу достает ключ из Map если такой ключ есть то он отображает ошибку и инпута и удаляет этот ключ и Map и цикл повторяется. NetworkError.setupUnexpectedErrors() - Функция расширения sealed class NetworkError который просто проверяет тип Unexpected и если да то отображает ошибку в виде Toast. Далее мы вызываем эти методы в SignInFragment На этом все в результате все будет выглядить вот так. P. S. Конечно же это все чисто для примера. На странице авториации никогда нельзя отображать ошибку в инпутах, все должно отображаться в Toast, но это все для примеров :). Снизу ссылки на репозитории. Репозиторий где будет этот проект: github.com/TheAlisher/Boilerplate-Sample-Android. Код из статьи находиться в этой ветке. Основной репозиторий самого Boilerplate: github.com/TheAlisher/Boilerplate-Android  Kotlin разработчик