Обновить

Комментарии 9

Спасибо за статью, как раз вынужден оперативно разбираться с авалонией и мввм, очень доступно разложили. Было бы здорово, если пример о навигации был бы более развернутый

Поставил бы лайк, да карма не позволяет :(

Будет серия статей про mvvm. И про навигацию тоже будет подробная статья. Постараюсь за следующую неделю все опубликовать. Следите. Спасибо за отзыв.

Карму немного исправил :)

enum State {        
    case idle        
    case loading        
    case loaded([Product])        
    case error(String)    
  }

Вместо россыпи разрозненных свойств я предпочитаю использовать единый State. Идеальный инструмент для этого - enum.

вы видимо не сталкивались с более сложными view model, где нужно данные из одного State case перегонять в другой.

Leaky Views: Передача UI объектов во ViewModel. Никогда не передавайте UIImage или NSAttributedString. Передавайте Data или просто String. ViewModel должна жить в мире чистой логики.

не совсем понял. Вы предлагаете запретить передавать UI в модель или чтобы модель не использовала swiftui и uikit в целом?
А еще что вы предлагаете делать, когда у вас NSAttributedString состоит из нескольких частей с разным форматированием, которые нужно еще собрать в одну строку перед тем, как показать?

Навскидку не придумал для чего мне могло бы понадобиться передавать данные из одного State case в другой. Но поразмыслю над этим, спасибо.

А про запрет UI во ViewModel - да, если у вас есть необходимость импортить UIKit во viewModel, то это порочная практика. Именно об этом и пишу. Этого всегда можно избежать. В случае с атрибутированной строкой можно написать отдельный helper, который помогал бы решить эту проблему и дергать его из ViewModel. Кмк это бы решило проблему чистого ViewModel.

Соглашусь с первым комментарием по поводу состояния.

"Состояние"("State") – это прям отдельный шаблон из книжки Банды Четырех, чтобы с ним работа была комфортна, он должен реализовываться через набор классов с одним и тем же интерфейсом. Иначе, рано или поздно столкнетесь с проблемами. Например, enum'ы не расширяемые. Добавить состояние без нарушения OCP у вас не выйдет. В мелких проектах это и не важно, но в крупных...

Ну, и Вы тут много говорите о том, что не нужно смешивать ответственности, а "Состояние" (с переходами между ними, которые довольно часто не тривиальные! и не все вообще разрешены!) – вполне себе цельная отдельная ответственность...

Попробуйте её инкапсулировать по шаблону Банды Четырех. Не с первого раза, но получится всю прелесть прочувствовать...

Вот вот. Это какой то идеальный случай. Так же еще и координаторы продвигают.
Для примеры:
А вот еще продукт можно добавит в фейворитс, но только если вы залогинены, а если нет предложить пользователю или залогиниться или зарегистрироваться в форме из 7 экранов и потом в случае успеха, загнать на сервер и в случае успеха и обновить список продуктов с закрашенным сердечком. И все это красивое начинает потихоньку расползаться. И стоит задуматься а стоит ли вью модели знать о продукте например и/или сколько вью моделей вам нужно на представление. Почему например не по вью модели на продукт или на кнопку фейворитс отдельно, тоже представление ведь, только маленькое?
По NSAttributedString тоже не ясно. Вы предлагаете сложные строки где форматировать? В мейн потоке во время отрисовки ячейки? Что б все дергалось при скроллинге?
А если опять вернуть к скроллингу то часто на сложных коллекциях надо еще что то прирендерить, а может еще и UIImage через NSAttachment в NSAttributedString вставить потому что дизайнер хочет красивые кавычки или буллеты.
В общем, если бы автор мне так на собеседовании рассказал что и как нужно делать в контроллере представления, я б наверное его не нанял.

Сам тоже сейчас редактирую статью про "правильную готовку" MVVM. Думал уже не публиковать, но, видимо, теперь придется :)

В статье Вики про MVVM указано, что вьюмодель содержит всего 3 ответственности:

  1. абстракцию для разделения вьюхи и модели (слабая связность).

  2. преобразование данных из вида модели к виду отображения (и возможно, но необязательно, обратно).

  3. биндинг - вариация абстрагирования и/или преобразования данных.

Вот, в принципе и все, что имеет право быть во вьюомдели. Все остальное, что вы перечислили, это уже либо модель, либо представление. Без вариантов! Например, навигация – ответственность представления. Если Вы меняете систему представления (например, с iOS переезжаете на мак, часы или телевизор), то в Вашем приложении не нужно бы менять ни вьюмодель, ни модель... а если Ваша модель зависит от роутера или координатора, то вам этого не избежать... а вот если роутер или координатор дергаются напрямую из вью, то остальные два слоя вашего приложения изменений не требуют – достаточно поменять только представление...

Unidirectional Data Flow – это пункт 2 из обязательных ответственностей. Преобразование данных. В одну и в другую сторону. Они в 142% случаев всегда независимы друг от друга, да. Поэтому реализовать их в двух разных классах не представляет сложности никогда. Но при этом оба класса все же остаются внутри слоя вьюмодели. Это слой. Он не обязан состоять из одного класса... В вашем примере Вы просто лишь одну часть этой ответственности осуществляете через биндинг. Но можно и обе, проблем не будет, это все еще будет UDF, если вью будет писать в одно свойство, а модель в другое (в одном или разных объектах – уже неважно)...

Но в принципе биндинг для преобразования данных не обязателен, как и для абстракции. Но с его помощью можно реализовать и то, и другое. А можно и не реализовывать. Биндинг для MVVM не обязателен, но тогда оно выродится в MVP или MVC с пассивной моделью.

В остальном, Вы молодец. Копайте глубже и проблем у Вас не будет ни с какой архитектурой. Еще и на лету их сможете менять, если ответственности представления (типа навигации) не будете опускать ниже.

ПС: неплохо Вы статьи генерите – по одной в день – ИИ помогает?

Текстов накопилось за последние годы работы пачка. На месяц писанины хватит хоть через день публиковать. А там уже и к новым исследованиям подключусь. Самая сложность разбить на читаемые куски и не потерять суть. Подумываю над тем как линковать в статьи ссылки на Github, что бы код не раздувал статьи. Но пока идеи на этот счет не очень. Поэтому статья с координатором вышла огромная. А ИИ «помог» только заголовок для этой статьи сляпать. Минусов напихали, больше не буду :)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации