Гибридная архитектура на основе MVVM и TCA для iOS
Привет! Нашел интересную , в которой автор рассказывает, как они адаптировали архитектуру под крупный проект с навигацией на UIKit и интерфейсом на SwiftUI. Главная цель - избавить разработчика от головной боли при работе с многопоточностью, оставив только верстку и бизнес-логику.
Что взяли за основу:
Из MVVM оставили Model, View, ViewModel. Из TCA добавили State, Action и Reducer.
State - центральное хранилище состояния всей иерархии View. Все UI-параметры, которые раньше лежали во ViewModel, вынесены сюда. State - класс, всегда один экземпляр, удобно передавать между компонентами.
View может читать State и менять его через Binding. Если изменение не через Binding (например, нажатие кнопки) - обновление идет через ViewModel, но без прямого доступа к ней. Вместо этого используется Reducer.
Reducer - вспомогательный класс, который скрывает асинхронность ViewModel (она сделана актором). Вместо Task { await viewModel.handle(...) } - просто передача Action. Код становится чище.
Action - enum, описывающий все возможные взаимодействия View с ViewModel. View не вызывает методы ViewModel напрямую, только отправляет Action.
ViewModel - бизнес-логика, сервисы, роутер. Единственный публичный метод - handle(_ action: Action). Все остальное приватное.
Module - файл, где собраны все протоколы модуля.
Как упростили работу с дочерними вью:
Если у дочерней вью много входных параметров, их выносят в структуру ViewState внутри самой вью. Вместо длинного инициализатора - одна строка.
Аналогично с замыканиями: вместо десятка отдельных замыканий - один enum Action и одно замыкание onAction. Внутри вью просто вызывается onAction с нужным кейсом.
Для списков с разными ячейками - передают в List сразу весь State и Reducer. Внутри уже выбирают нужные данные для каждой ячейки.
Архитектура работает и со SwiftUI и с UIKit - разница только в механизме отслеживания изменений State.
Вывод:
Автор предлагает гибридный подход, который сочетает простоту MVVM и стройность TCA. Основные плюсы: чистый код, отсутствие лишних асинхронных конструкций, удобная работа с дочерними вью и поддержка обеих UI-фреймворков (UIKit и SwiftUI). Полный код можно посмотреть на .