Привет, Хабр!    Меня зовут Игорь, я Head of Mobile в компании AGIMA.  Через нас проходит много проектов и оценок, функционал там зачастую повторяется, поэтому я решил показать, как мы решаем типовые задачи, и поделиться этим с вами. Начнем мы с самого начала. Как правило, началом для приложений служит авторизация. Рассмотрим классический случай с вводом номера телефона и смской и остановимся подробнее на экране подтверждения смс.  Важно: в примере кода на github будет полноценный пример с вводом номера телефона и кодом, но экран ввода номера телефона совсем скучный, поэтому сегодня мы вводим код :) Выглядит не очень сложно, но, если присмотреться, функционал экрана довольно большой, а именно: отправить код на сервер; включить таймер повторной отправки + отобразить визуально; после завершения таймера показать кнопку «отправить еще раз»; отправить повторный запрос на получение кода; отобразить все ошибки; обработать успешное подтверждение кода. Если попробовать разделить экран на UI и логику, получается примерно такое взаимодействие между логикой и интерфейсом. Можно, конечно, отправить всю логику про таймеры и isLoading на View слой, но мне больше нравится относить это к логике. Особенно учитывая то, что я большой поклонник MVVM+Rx (и буду это использовать в статье), это более чем уместно смотрится. Ну да ладно.   ViewModel в этом случае играет роль некоего «преобразователя» пользовательских действий: у нее есть input и output (видно на картинке выше). За навигацию будет отвечать «кто-то еще», например, координатор. Со стороны UI нам будут интересны следующие компоненты: ViewModel  будет выглядеть так:   Обратите внимание, что при таком подходе мы стараемся не использовать PublishSubject, BehaviourRelay итп, чтобы четко разделить input и output у ViewModel.  Теперь давайте это все свяжем. View отдает следующие потоки данных: ViewModel будет как-то (покажу ниже) обрабатывать ввод кода пользователя, а также делать запрос на повторную отправку кода, если мы нажмем на кнопку. Сначала давайте посмотрим ViewModel целиком, далее разберем ее более подробно. ViewModel рассмотрим «по кусочкам»:   _codeSubject  — это поток данных из textfield ввода кода.  validCodeObservable  — отфильтровывает значения нужной длины, которые мы будем отправлять на сервер.   Выше мы договорились, что  PublishSubject не используем, но внутри нам от того же кода нужен не только AnyObserver, но и Observable , чтобы использовать его, например, для отправки кода на сервер. В дальнейшем я планирую использовать такую технику: AnyObserver или Observable в публичном интерфейсе и PublishSubject внутри. Собственно, отправка кода на сервер :) Обращаем внимание на .materialize(). Поскольку мы планируем использовать этот Observable в реактивных цепочках, мы не хотим получить ошибку и прерывать их. materialize позволяет завернуть все значения и ошибки в Result<Value, Error> и тем самым мы никогда не прервем реактивную цепочку из-за ошибки.    Ранее я описывал другой вариант с помощью RxAction, его также можно использовать для создания потоков событий значений, ошибок и isLoading. Здесь довольно интересный момент. Если мы получили валидный код, готовый к отправке, то мы отображаем интерфейс загрузки. Если мы получили ответ от сервера, это означает, что нам надо скрыть состояние загрузки. Таким образом, мы можем взять эти потоки данных (на примерах выше), смаппить их в true или false и забиндить в isLoading.   didAuthorize = codeEvents.elements()...   .elements() работает как фильтр и пропускает только значения из codeEvents и игнорирует ошибки. Напомню, что тип значений у codeEvents — это Result<Void, Error> , что является частью RxSwiftExt. Таймер  включается при следующих событиях:    мы отправили код на подтверждение (validCodeObservable.mapTo(Void())); мы перезапросили код (didRequestNewCode); сразу же при заходе на экран (.startWith(Void())). Именно это описано в строчке Observable.merge... Сам таймер делается стандартными средствами RxSwift. Останавливаем таймер с помощью оператора take(while:), пока значение таймера не станет равно 0.  Лейбл с таймером и кнопка «переотправить» должны скрываться/показываться в зависимости от того, активен ли таймер: За ошибки отправки и запроса нового кода у нас будет отвечать один поток данных errors.   Также запретим редактировать код, во вркмя того, как он отправляется: ViewModel получилась довольно-таки тестируемая, поэтому давайте напишем тесты! Я приведу примеры тестов, которые будут показывать, как ViewModel реагирует на пользовательский ввод. Создадим вспомогательный метод, который будет создавать поток событий ввода кода. Внимание, используется RxTest!   Например, таймер отправки нового кода должен запускаться и корректно отрабатывает сразу после открытия экрана —  напишем вот такой тест:   Или вот такой: проверим, что у нас передается на UI событие об ошибках   Полный код тестов, да и вообще весь пример можно найти тут. Требования могут слегка меняться от проекта к проекту (например, код можно отправлять по кнопке а не автоматом), но этот код достаточно несложно приспособить к подобным изменениям. Ну, а если вам понравился наш подход, вам интересны большие проекты по мобильной разработке, тогда  напишите нам.