
Введение в VIPER
Если вы какое-то время разрабатывали приложения для iOS, то заметили, что большая часть вашей логики находится внутри подклассов UIViewController. Это обычно называется проблемой Massive ViewController. Просмотр - это довольно простой объект, который заботится только о том, чтобы рисовать себя на экране. Модель - это скучное хранилище данных, которое может быть представлено простой коллекцией или сложной структурой данных.
По сути, вся остальная логика должна находиться внутри этого бедного одинокого ViewController. Он преобразует объекты модели в более удобное для просмотра состояние и наоборот. Он также обрабатывает ввод данных пользователем, извлекает данные, выполняет сетевые вызовы, выделяет и представляет другие ViewController. Справедливо ли, что все это удовольствие достается ViewController?
Не только удобочитаемость ViewController страдает от лишнего веса (в строках кода), но и тестируемость, ремонтопригодность и скорость рефакторинга. Учитывая, что Swift быстро меняется, а Apple выпускает новую iOS каждый год, поддержка и рефакторинг ViewControllers - огромная часть вашей работы как разработчика iOS. И проверяемость здесь действительно важна, потому что вам не нужны эти пугающие регрессии.
Поэтому некоторые умные люди решили улучшить MVC (модель-представление-контроллер) таким образом, чтобы ViewController в основном сидел на диете. MVVM (Model-View-ViewModel) была первой попыткой. Он освобождает ViewController от необходимости выполнять преобразование между объектами модели (бизнес-объектами, если хотите) в удобные для просмотра (с этого момента назовем их ViewModels) объекты. Такой подход улучшает тестируемость и упрощает ViewControlles, но достаточно ли этого? Он не касается реакции на действия, работы в сети, навигации и т. Д. Поэтому люди решили, что им нужно освободить ViewController и от этих других вещей.
Прежде всего, они создали Presenter (ViewModel в случае MVVM). Этот класс отвечает за обновление пользовательского интерфейса. Он принимает бизнес-объекты и переводит их во ViewModels.
Откуда он их получает? Из другого класса под названием Interactor, который отвечает за получение бизнес-объектов отовсюду: сеть, постоянное хранилище и передачу их в Presenter. Он также отвечает за реакцию на действия пользователя. Поэтому, когда вы нажимаете кнопку входа в систему, ваш ViewController передает его Interactor, и он выполняет сетевой вызов (обычно вы хотите сделать это в отдельном объекте, подробнее об этом ниже), ожидает ответа, а затем принимает решение. куда вам нужно идти дальше.
Поэтому, когда Interactor принимает это решение, он вызывает другой объект, называемый Router (вы можете найти Wireframe в некоторых статьях), который выполняет переходы или представляет ViewControllers модально.
Вы можете спросить, что осталось от ViewController? Помимо общения с Presenter, он еще много чего управляет. Поскольку мы играем на территории UIKit, мы не свободны от его влияния. Я считаю, что вы должны назначить свой ViewController делегатом и источником данных для различных вещей. Поступая так, вы не будете делать сумасшедших вещей со своим Interactor. Не пытайтесь взломать UIKit. Это невозможно и ненужно.
Итак, у нас есть V для просмотра, I для Interactor, P для Presenter, R для маршрутизатора. Итак, что означает E в VIPER? E означает объект. Сущность - это то, что вы называли моделью в MVC - скучное хранилище данных. Лично мне кажется, что он был помещен в VIPER просто для того, чтобы это круто звучало. Ваш Interactor будет получать эти объекты из различных источников, но также будет выполнять сетевые вызовы или выполнять некоторые долгосрочные задачи, используя службы - объекты, которые инкапсулируют это вид задач. Например, вы можете создать TasksService в приложении ToDoList (его должен сделать каждый разработчик iOS), который будет выполнять сетевой вызов к серверу, преобразовывать ответ в бизнес-объект и выдавать его обратно в Interactor. Допустим, вы найдете в ответе fireDate. Он будет похож на этот формат: «1994–11–05T08: 15: 30–05: 00»,, но в Просмотр вы должно отображаться что-то вроде «осталось 2 часа». Interactor передает объект Date в Presenter, который преобразует Date в String и передает его ViewController.
Но здесь мы упускаем какой-то важный элемент. Для каждого ViewController требуется Interactor, Presenter и Router. Все эти вещи общаются друг с другом посредством широкого использования протоколов. Итак, вам нужно собрать всех этих ребят и познакомить их друг с другом. Это делается с помощью объекта под названием Конфигуратор или Сборка.
Итак, теперь у нас есть все необходимое для создания одной изолированной части архитектуры VIPER под названием Модуль.

Вы должны знать, что не существует такого понятия, как обычный модуль VIPER. Некоторые разработчики связывают Router с Presenter вместо Interactor, другие используют отдельный объект, который работает с Entities и т. Д.
Используем VIPER около 3 месяцев. Можно сказать, недолго, но достаточно, чтобы увидеть подводные камни.
Хорошее
- VIPER - это попытка применить ТВЕРДЫЕ принципы на платформе iOS. Это не только архитектура, но и свод правил и соглашений. И это так полезно. Когда вы к этому привыкнете, пути назад уже не будет. Просто попробуйте, вам понравится.
- Готовность к тестированию. Поскольку модули имеют слабую связь, их действительно легко протестировать по отдельности. Каждый объект внутри модуля также достаточно хорошо разделен, что отлично подходит для модульного тестирования. Охват тестированием 80–90% легко достичь по сравнению с MassiveViewController, который практически невозможно протестировать.
- Поскольку модули независимы, VIPER действительно хорош для больших команд (в случае iOS это более двух разработчиков). Это означает меньше конфликтов слияния, лучшую тестируемость и более легкую замену модулей. Вы можете сначала создать начальный каркас архитектуры, а затем передать модули один за другим другим разработчикам для реализации логики.
- Кодовая база похожа. Как только вы начнете понимать философию VIPER, вы будете намного быстрее читать чужой код. Файлы будут меньше (не больше 3К строк кода UIViewControllers), логика станет более понятной, а общая стабильность и гибкость выше. Вы даже можете сделать кросс-платформенный анализ кода бизнес-логики с командой Android. Как это круто!
Плохо
- VIPER не вписывается в парадигму Apple UIKit. Невозможно легко разделить бизнес-логику и логику представления между Interactor и Presenter. Сторонние SDK также часто не подходят для VIPER. Так что приходится много чего обсуждать с коллегами, пробовать разные подходы и экспериментировать. На самом деле это звучит забавно для команды разработчиков, но попробуйте продать это группе разработчиков.
- Практически ничего не говорится о документации, передовом опыте и сообществе. Помимо нескольких статей все, что у вас есть, это Book Of VIPER, написанная на русском (на данный момент переведено на английский только 10%) и ничего больше. Поскольку первая статья о VIPER, которую мне удалось найти, была опубликована в 2013 году, можно догадаться, что это не такая популярная тема.
- Новому разработчику следует потратить пару дней на изучение основ VIPER, а затем пару недель на ознакомление с ним. Дополнительное время для каждого запроса на включение, больше времени для исследования и обсуждения. И в конце концов, все эти проблемы никуда не денутся. Через некоторое время у вас будет ряд соглашений и практических рекомендаций, но всегда будут новые проблемы.
- View, Interactor, Presenter, Entity, Router, Configurator… Вы можете догадаться, что это много файлов. В два раза больше с тестированием. Делать их каждый раз вручную будет пустой тратой времени. Вот и умные люди создали автоматический генератор кода для VIPER наподобие Generamba. Это не только поможет вам сгенерировать все файлы, но также протоколы и общие функции.
Уродливый
- Связь между модулями. В классических модулях VIPER должны взаимодействовать с докладчиками. Я не согласен с тем, что это нарушает единую ответственность. Поскольку вся суть VIPER - это независимые модули, нам нужно найти способ передачи данных между ними, не зная подробностей об их реализации. Наверное, лучший способ справиться с этим - ViperMcFlurry. Но если вы заглянете внутрь его кода, то увидите, что он основан на переключении методов и связанных объектах. Разве здесь не что-то слишком сложное?
- В конце концов, вам нужно будет создать версию VIPER, которая соответствует вашим потребностям. Через некоторое время вы заметите некоторые подводные камни в архитектуре VIPER: некоторые модули не нуждаются в просмотре, докладчики остаются неиспользованными и т. Д. Ребята из Uber проделали большую работу и создали архитектуру RIBs.
Вместо заблуждения
VIPER выглядит блестяще на бумаге: принципы SOLID, TDD в своем роде, разделение кода, масштабируемость и т.д. Но мы живем в реальном мире. У нас есть сроки, нам нужно быстро выполнить итерацию и как можно скорее отправить наш продукт клиентам. Можно сказать, что это вина менеджеров по продукту, дизайнеров или даже клиентов. Я частично согласен с этим. Мы, как разработчики, должны объяснять менеджерам, что скорость ≠ качество и рано или поздно технический долг станет проблемой. Вот почему я не перечислил это в разделе Плохие.
VIPER действительно хорош, когда у вас много времени, а ваше приложение не сильно меняется. Я настоятельно рекомендую вам попробовать VIPER. После этого вы станете гораздо лучшим разработчиком. В вашем стороннем проекте, возможно, в каком-нибудь небольшом вспомогательном приложении. Сначала это будет намного медленнее, но через некоторое время вы сэкономите (!) Время по сравнению с MVC. Только после этого начните применять VIPER на своем рабочем месте.
Если у вас есть вопросы, то здесь мой Твиттер или просто ответьте здесь.