Connect with us

Разработка

Убираем M из MVVM в SwiftUI

Главное — свести код представления к минимуму, а как только он становится слишком большим для одного представления — разделить его!

Опубликовано

/

     
     

Я так часто сталкиваюсь с этим вопросом, что наконец-то хочу написать о нем. Это не будет длинный пост об архитектуре iOS-приложений, и это даже не будет провокационный быстрый комментарий. Это просто то, как я создаю iOS-приложения в эти дни, особенно Ice Cubes, мой SwiftUI клиент Mastodon с открытым исходным кодом. Если вы достаточно хорошо инкапсулируете свой код, ваши View — это просто представления состояний, не меньше и не больше.

Недавно я добавил новую функцию, и благодаря всем инструментам, которые я уже собрал в Ice Cubes, мне потребовалась всего пара часов, чтобы соединить все вместе. Речь идет о поддержке новой функции фильтрации уведомлений, которую Mastodon добавил в свой бэкэнд и веб-фронтэнд. В этой статье я не буду демонстрировать функцию, поскольку для нас интересен код.

При написании кода для этой функции я фактически вернулся к кодовой базе после нескольких недель, в течение которых я к ней не прикасался. Одной из целей было свести этот код к абсолютному минимуму. Это один из единственных способов сохранить рассудок при работе над моими проектами с открытым исходным кодом. Не поймите меня неправильно, я люблю это занятие, но с учетом рабочей и семейной жизни сведение лишнего кода к минимуму доставляет массу удовольствия.

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

Первые две строки посвящены получению двух сред — первая Client предоставляет представлению все необходимые высокоуровневые функции для выполнения сетевых запросов к Mastodon. Вторая Theme предлагает все необходимое для настройки внешнего вида представления.

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

Теперь давайте посмотрим, как это представление рисуется на экране:

Вы наверняка заметили, насколько коротким получился код для представления, показывающего три разных состояния. Это потому, что один из лучших способов убрать «М» из MVVM — разбить представление на маленькие, простые структуры. Apple построила SwiftUI именно таким образом: State, Binding и Environment позволяют вашим представлениям взаимодействовать друг с другом прямолинейно.

Когда представление загружается, я использую встроенный ProgressView. Если возникает ошибка, я использую свой собственный ErrorView, который предоставляет кнопку для повторной попытки. Наконец, если есть данные, я отображаю их с помощью другого представления, и я добавил несколько жестов пролистывания, чтобы пользователь мог взаимодействовать с запросами уведомлений.

Последняя часть этого представления содержит некоторые функции синхронизации, которые это представление будет использовать. Код хорошо инкапсулирован, поэтому его наличие в представлении абсолютно нормально. Client высокоуровневый и тестируется независимо, поэтому наличие однострочных запросов на уровне представления не является проблемой.

Некоторые улучшения могут заключаться в добавлении управления ошибками в функции acceptRequest и dismissRequest. Если запрос не удается, пользователю может быть представлено предупреждение, чтобы он мог повторить попытку. Мы также могли бы сделать некоторое оптимистичное поведение и удалять запросы из текущих отображаемых, как только пользователь провзаимодействует с ними. И чтобы было понятно, весь этот код по-прежнему будет находиться в этом представлении.

Теперь давайте посмотрим на NotificationsRequestsRowView, это чистое представление без особого взаимодействия. Но это еще один пример представления с чистым состоянием. В это представление передается статический объект, и единственная цель этого представления — отобразить его.

Теперь давайте перейдем к вопросам, которые люди задают мне снова и снова.

Как вы это тестируете?

Какое значение имеет тестирование представлений для вашего проекта? Что вы хотите протестировать? Поскольку ваши представления — это просто выражения состояния, лучшие тесты — это snapshot  тесты. Если вы создали превью для ваших представлений, то тестирование моментальных снимков — это, по сути, то же самое. Введите состояние в представление, сделайте его скриншот и сравните этот скриншот с новым в следующий раз, когда захотите его протестировать.

При модульном тестировании следует тестировать все ваши строительные блоки, в моем случае Client и Theme, например. Если они работают правильно, то и мое View будет работать правильно.

А с окружениями можно легко построить их таким образом, чтобы вы могли внедрять различные реализации для превью, тестов, продакшена и т.д… Окружения — это буквально бесплатная инъекция зависимостей для всей иерархии представлений.

Но этот паттерн работает только для простых REST-приложений…

Не совсем. Конечно, Ice Cubes — это клиент Mastodon, но он также имеет тонну функций на стороне клиента, широко использует SwiftData и предоставляет множество функций, не связанных с API Mastodon. Главное — свести код представления к минимуму, а как только он становится слишком большим для одного представления — разделить его!

Прощай MVVM, слава VV 🚀.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: