UIKit Viper або MVC здорової людини

Близько року тому я познайомився з таким чудовим патерном, як Viper . І тепер хочу розповісти про свої проблеми, а також про їх вирішення.

Начитавшись туторіол про Viper я почав пробувати створити на ньому новий проект. У результаті мій Viper виглядав приблизно так:

viper

Поруч із кожним блоком підписано базовий клас. Стрілки позначають посилання між об'єктами (пунктиром — weak).

Ця схема не вирішує вкрай важливої ​​проблеми. Як розташовувати модулі в UINavigationController? Через те, що стек навігації необхідно додавати спадкоємців класу UIViewController.

  • доведеться в router простягати view через presentr і явно вказувати, що це UIViewController
  • після додавання view в UINavagationController, у view лічильник сильних зв'язків збільшиться до 2-х. Потім доведеться вручну прибирати з пам'яті viper модуль.
У такому підході очевидна деяка несумісність Viper та UIKit.

Як подружити Viper та UIKit

Насамперед необхідно зрозуміти, як перейти від Apple MVC до Viper. MVC досить легко розкладається на view, presenter та interactor. Але у viper з'являється ще додатковий шар – router. І на цьому моменті я пропоную альтернативну точку зору (принаймні на просторах інтернету я нічого подібного не бачив).

viper

Стало помітно більше UI компонентів. Тепер давайте розберемося, чому тут все так, чи майже. Як основа для своїх міркувань, я скористаюся цією статтею.

Presenter це PONSO, який в основному складається з логіки, щоб управляти UI. Він збирає вхідні дані від взаємодії з користувачем, таким чином, він може надсилати запити Interactor'у.Presenter також отримує результати Interactor'а і перетворювати результати на стан, який є найбільш ефективним для відображення на View.

Entity ніколи не передаються з Interactor'а до Presenter'у. Натомість прості структури даних, які не мають поведінки, передаються з Interactor'а до Presenter'у. Це перешкоджає будь-якій 'реальній роботі' в Presenter'і. Presenter може лише підготувати дані для відображення на View.

Якщо як presentr взяти не PONSO, а UIViewController, що зміниться? Так presentr почне відповідати за відображення модуля, але під капотом UIKit. Всі методи життєвого циклу можна розцінювати за таким же принципом, як події від кнопок view. Тобто view повідомляє presentr, що стався viewdidload і з цим можна щось зробити. Вся логіка малювання залишилася на view.

Тепер давайте розглянемо router(у статті зображено, як wireframe. Я зазвичай поділяю на router і moduleManager)

Маршрутизація обробляє навігацію від одного екрана до іншого, як визначено у wireframes, створених проектувальником взаємодії. Wireframe об'єкт відповідає за маршрутизацію. Wireframe об'єкт володіє об'єктами UIWindow, UINavigationController, і т.д. Він відповідає за створення Interactor, Presenter та View/ViewController та за налаштування ViewController. Оскільки Presenter містить логіку, щоб реагувати на введення даних користувачем, Presenter знає, коли перейти на інший екран. Wireframe знає, як це зробити. Отже, Presenter – це користувач Wireframa.

Постає питання. Чому router не може сам бути UINavigationController? Навіщо нам ще велосипед, коли в UIKit є свої роутери - це UINavigationController та співчуваючі. У подібних класах вже закладено основну логіку.

Що вирішує такий підхід?

  • менше коду, все зроблено за нас
  • зрозумілий код, для людини, незнайомої з Viper
  • за роботу з пам'яттю тепер відповідає UIKit, руками не доведеться знищувати модулі
  • проста інтеграція зі сторонніми бібліотеками, які на вході приймають UIViewController
Як приклад я створив невеликий демо проект. Сподіваюся на об'єктивну дискусію.

Хардкорна конфа за С++. Ми запрошуємо лише профі.