Skip to content

Переходы между модулями

Andrey Zarembo edited this page Nov 13, 2015 · 1 revision

Переходы между модулями - введение

Теперь я расскажу про переходы между модулями и передачу данных между ними. Подход к созданию модулей через фабрику, описанный в статье про VIPER на Objc.io неудобен тем, что каждому модулю нужно было задавать ссылку на фабрики следующих модулей, а также добавить то, что будет выполнять переход между модулями. Т.е. практически заново создать Segue.

Мы решили попробовать родные StoryboardSegue для переходов между модулями. Открывались заманчивые перспективы: Для перехода в другой модуль необходимо было бы указать SegueID для самого перехода и передать в модуль данные для работы. Для конфигурации модулей мы используем Typhoon, поэтому все модульные ViewController после инициализации через Segue уже имеют связи с другими компонентами модуля.

Переходы между модулями - через ViewController

Это самый первый и простой вариант. У роутера вызывающего модуля есть ссылка на свой ViewController, при переходе на другой модуль у ViewController вызывается метод PrepareForSegue, где в Sender передаются данные для следующего модуля. Внутри prepareForSegue вызывающего ViewController эти данные передаются во ViewController следуюшего модуля.

Это работает, но есть некоторые недостатки:

  • Логика настройки следующего модуля размещается внутри View, а не в Router
  • Нет универсальности и переиспользования, prepareForSegue надо делать в каждом модуле.
  • Данные для работы следующего модуля попадают во View, а не в Presenter
  • Каждый модуль знает об устройстве другого модуля
  • Каждый роутер знает, что работает с ViewController и схема работает только для этого.

Переходы между модулями - через ViewController c блоком конфигурации

Для решения первых двух проблем были использованы... свизлинг и блоки! В PrepareForSegue в Sender отправляется блок, в котором выполяется настройка модуля через DestinationViewController. В засвизлено методе PrepareForSegue проверяется, если это блок, то он вызывается с destinationViewController из segue в качестве параметра.

Это работает, логика настройки следующего модуля находится целиком внутри Router, больше не надо для каждого модуля добавлять во ViewController метод prepareForSegue, но остаются три остальных проблемы

  • Данные для работы следующего модуля попадают во View, а не в Presenter
  • Каждый модуль знает об устройстве другого модуля
  • Каждый роутер знает, что работает с ViewController и схема работает только для этого.

Переходы между модулями - много протоколов

Чтобы решить оставшиеся проблемы были использованы протоколы. Много протоколов. А также, свизлинг и мини-вариант промисов. В итоге получилась система передачи данных между модулями без тех недостатков, данные из презентера отдаются роутеру и он конфигурирует ими презентер следующего модуля. Но появились два новых недостатка:

  • На освоение у нового разработчика уходило порядка 2х дней
  • Данные передавались в одну сторону

Переходы между модулями - вариант с ModuleInput

Текущий вариант, доступный в нашем Github под названием ViperMcFlurry стал гораздо проще в освоении. У каждого модуля теперь есть точка входа - ModuleInput, которая позволяет настроить модуль или вызывать методы. Этот moduleInput можно использовать внутри роутера для настройки модуля, можно вернуть презетеру, для постоянной связи с подмодулем. У каждого модуля можно задать ModuleOutput, чтобы вернуть данные из модуля. ModuleInput/Output - это протоколы, которые задаются внутри модуля, т.е. в нем хранится контракт связи с ним. В большинстве модулей в роли ModuleInput выступает презентер этого модуля, а в качестве ModuleOutput - презентер вызывающего модуля.

Embed Segue

Поскольку PreformSegue требуется только имя перехода, а PrepareForSegue засвизлен, а Typhoon настраивает модуль по ViewController, мы можем использовать любые классы Segue и механизм переходов между модулями будет работать.

Поэтому для встраивания модулей бы создан EmbedSegue. Внутри perform у SourceViewController вызывается метод, который возвращает View для идентификатора Segue. В этот View и встраивается модуль.

Cross Storyboard

Но у Segue есть проблема, до iOS 9 они работали только внутри одной Storyboard, а нам нужно поддерживать iOS 7/8. При этом на проектах работает несколько разработчиков и нужно разделять Storyboard, чтобы уменьшить количество конфликтов при слиянии ветов. Так был создан PlaceholderViewController и специальная категория для Segue, которые позволяют сделать переходы между контроллерами в разных Storyboard. У Segue, конечно же, засвизлен метод destinationViewController, который проверяет класс целевого контроллера. Если это PlaceholderViewController, у него запрашивается целевой viewController. Placeholder создает целевой контроллер по своему RestorationID, имеющему формат Имя_сториборды@имя_контроллера. Создает Storyboard, из неё инициализирует контроллер и возвращает в Segue.

Это позволяет использовать все классы Segue, в том числе стандартные и Embed, при этом разбить Storyboard на несколько и использовать передачу данных между модулями.