Не так давно я публиковал перевод Почему VIPER это плохой выбор для вашего следующего приложения. И, как это часто бывает, мнение переводчика не совпало с мнением автора оригинальной статьи. Поэтому в этом посте я коротко расскажу как я пытаюсь (пока еще пытаюсь) внедрить VIPER в свои проекты.
Когда я начал работу над своим предыдущим проектом, в команде было ровно два целых ноль десятых мобильных разработчика: один писал версию под Андроид, второй – под iOS.
Естественно, iOS версия создавалась на классическом, рекомендуемом самим Apple, паттерне MVC.
У меня была View: "любимый" сториборд, в котором было over9000 довольно много экранов, и который был похож на это:
У меня была модель – классы, для хранения настроек, данных пользователя, некоторых данных, которые сохранялись во внутренней БД.
И у меня был вью-контроллер для каждого экрана на сториборде. Во вью-контроллере должны были размещаться обработчики действий пользователя, а также уведомления от модели (получение данных по запросу или изменения данных). Но на самом же деле там происходило нечто вот такое:
Networking.request(.POST, withURL: serverAddress, andParameters: parameters) { (dictResult, error) in
if let dictResult = dictResult {
// some actions with view
if let rates = dictResult["rates"] as? [AnyObject] {
for rate in rates {
if let rateDictionary = rate as? JSONDictonary {
let result = Result(jsonDictionary: rateDictionary)
self.resultsArray.append(result)
}
}
}
// some actions with view
} else {
if let error = error {
DispatchQueue.main.async(execute: {
self.showError(error)
})
}
}
}
В общем: я уверенно двигался от Model View Controller к Massive View Controller и с этим нужно было что-то делать.
Я начал изучать возможные архитектуры для мобильных приложений, и конечно, мне сразу же понравился паттерн Clean architecture (он же, по сути – VIPER):
картинка из The Book of VIPER
Я смело потратил несколько выходных на изучение этого паттерна. Просмотрел несколько видео-туториалов и написал немало простых приложений-примеров на нем. Также нашел удобные скрипты для генерации фалов модулей, ведь один модуль это – около десятка файлов (протоколы, конфигураторы, непосредственно классы, сториборды, юнит-тесты). Но довольно быстро пришло понимание, что если я сейчас попробую внедрить VIPER “в лоб”, то потеряю массу времени. Ведь с непривычки, у меня будут слишком большие временные затраты на правильное разделение ответственности между всеми слоями. И я решил пойти путем попроще и разделять ответственность постепенно.
А что если я скажу тебе, что UIViewController — это View.
Именно поэтому я взялся за MVP. Самое сложное и в то же время – самое простое – понять, что UIViewController – относится к слою View. Он должен работать с UI и только с ним: принимать от пользователя действия связанные с UI (нажатия кнопок, ввод текста, etc.) и менять отображение самого UI. Сначала это казалось сложным, но я вывел для себя несколько простых правил, которые помогают положить во вью-контроллер все необходимое, и при этом не положить туда ничего лишнего:
Все методы и алгоритмы вью-контроллера должны работать с UI. Если вы видите какой-то метод или кусок кода, который может работать без использования UIKit, знайте: этому методу/блоку кода здесь не место.
Для презентера, все в точности наоборот: там не должно быть ни одного метода для выполнения которого нужен UIKit. Если такие методы обнаружены – нужно внимательно изучить эти участки кода и вынести их во вью-контроллер.
- У вью-контроллера и презентера есть протоколы на которые они подписаны, это их внешние интерфейсы. Там определены методы, которые можно вызывать для них извне. Поэтому, если вдруг, вы видите во вью-контроллере вызов метода из интерфейса (протокола) этого же вью-контроллера – этот метод должен находиться не во вью-контроллере, а в презентере.
Переход на MVP, научил меня главной вещи: разделять ответственность между сущностями. Пока что, я только отделил работу с UI от всего остального, но впереди, меня все-таки ждет VIPER. Возможно классический, а возможно и более многослойный.
Я надеюсь, что скоро я до него доберусь и продолжу свой рассказ.