Многообещающая ViewModel
Всем привет. Как и многие из вас, я недавно начал свое путешествие в новый и странный мир SwiftUI и Combine, и должен сказать, что по большей части мне очень нравится поездка. Да, конечно, он новый, яркий и молодой, но он также полон обещаний (объединить, подмигнуть). Я очень надеюсь, что эти инструменты хорошо созреют и наберут достаточную динамику, чтобы оставаться для нас, разработчиков iOS.
Но не будем отвлекаться, а сосредоточимся на цели этой статьи. Как и в случае с любыми новыми инструментами, требуется несколько проб и ошибок, чтобы выяснить, как лучше всего их использовать и извлечь из них максимальную пользу. С большой мощностью приходит и великая… (шепот на ухо говорит мне, что я на правильном пути).
В этой статье вы найдете мой взгляд на реализацию SwiftUI и Combine в MVVM в очень простом проекте.
Все инструменты и модули, которые вы найдете в проекте, предназначены только для того, чтобы продемонстрировать, как мы могли бы реализовать знаменитый шаблон MVVM, пытаясь избежать некоторых ловушек, вызванных новыми инструментами кодирования на языке iOS.
Но меньше разговоров и больше отображения кода.
Образец проекта
Как упоминалось ранее, проект, созданный для этой статьи, очень упрощен.
Инструменты и реализация функций далеки от готовности к производству и служат только для того, чтобы служить примером и основой для размышлений для дальнейшего улучшения.
Исходный код можно найти здесь: SpaceXSwiftUICombine.
Основная цель — показать пользователю все запуски SpaceX, добавив при этом несколько небольших функций обработки данных (поиск и фильтр).
Вот что вы можете найти, покопавшись в исходном коде:
- Объединить сети
- Единый источник достоверных данных (комбинация инструментов Combine / внедрение зависимостей)
- Координатор централизованной маршрутизации
- Множество инъекций зависимостей
- Множество протоколов: 0
- Некоторые функции поиска и фильтрации
Но в целом структура ядра должна выглядеть очень похожей на предыдущие реализации MVVM, с которыми вы, возможно, уже знакомы.
Основная особенность этого MVVM-роутера
Координатор централизованной маршрутизации
Если вы уже немного работали со SwiftUI, вы, должно быть, очень рано заметили, что представления очень тесно связаны друг с другом. Ссылки или листы навигации обычно вызывают следующее представление в потоке навигации по имени. Это делает любые будущие изменения в навигации очень сложными (особенно в сложной среде навигации приложений). Мой подход заключался в том, чтобы попытаться централизовать маршрутизацию в одном месте, которое может быть совместно использовано или передано любой модели представления.
Как вы можете видеть в приведенном выше коде, mainRouter основан на протоколах, которые помогают разделять представления. Это можно сделать, установив маршрутизатор в модели представления и вызвав его при необходимости в представлении, как показано в следующих двух примерах.
И вуаля, ваше представление не обращает внимания на то, что представляет routeToLaunches () или routeToRockets ().
Это также работает с маршрутизацией, которая требует совместного использования данных, как вы можете видеть на маршруте к DetailLaunchView.
Единый источник достоверных данных
Эта функция легко реализуется с помощью комбинации внедрения зависимостей, использования определенного репозитория для управления данными и объединения.
Это не ново, но работает хорошо.
Как и в случае с протоколами маршрутизации, мы используем возможности протоколов для уменьшения связи. Репозиторий может быть передан путем внедрения зависимостей в любую часть приложения. Просто подпишитесь на издателей, указанных в протоколе, и у вас будет доступ к последнему и самому свежему потоку данных.
Примеры стоят тысячи слов:
- Объявите свой репозиторий для обмена данными
- Ввести компонент
- Подпишитесь и назначьте или используйте отправленные данные. Очень хорошо работает в сочетании с переменной @Published.
- Затем вы можете применить к данным требуемые преобразования.
- Примените эти изменения к локальным данным по своему желанию.
В целом, реализовать эти типы методов с помощью SwiftUI, Combine и какого-то DI несложно. Если вам лень, существует множество библиотек внедрения зависимостей, которые должны помочь (Resolve, SwiftInject и т. Д.).
Легкость обработки данных
В качестве последнего указателя я хотел бы быстро перейти к прекрасному симбиозу, который уже существует между SwiftUI и Combine.
Для этого вы просто собираетесь сосредоточиться на реализации очень простой панели поиска и фильтров.
Выше показан простой вид сетки, отображающий запуски пространства X.
- Связывание между представлением и viewModel. В этом случае содержимое TextField и переменная запроса viewModel.
- Связывание между представлением и фильтром viewModel.
- Связывание отображаемых данных Gridview с запуском viewModel.
Далее давайте посмотрим, как обрабатывать входящие данные в viewModel.
Волшебство происходит в переменной filterLaunchesPublisher. Комбинируйте, устраняйте, обрабатывайте и назначайте. В очень небольшом количестве строк кода у нас есть полностью функциональный поиск и фильтрация наших данных.
- @ Опубликованные переменные, помогающие связывать данные между viewModel и представлением.
- Создание паблишера для обработки данных. В данном случае это комбинация нашего основного потока правды: запусков, запроса и примененного фильтра.
- Назначьте издателя обработки локальной ограниченной переменной для отображения актуальных данных.
Несколько советов и приемов, которые стоит попробовать
Я завершу эту статью несколькими советами и приемами, которые я усвоил во время работы со SwiftUI и Combine.
Назначить сильную ссылку
Во-первых, кажется, что комбинация assign создает сильную ссылку между элементами. Чтобы решить эту проблему, кто-то на forum.swift.org придумал небольшой симпатичный фрагмент кода. Я бы предложил использовать это.
Ленивая загрузка подвидов NavigationLink
Еще одна странная находка заключается в том, что представление, установленное в пункте назначения NavigationLink, не всегда загружается лениво. На самом деле кажется, что все совсем наоборот. Это может привести к тому, что ваше приложение будет загружать огромные объемы данных при запуске. И снова StackOverflow — наш друг. И один из наших коллег придумал красивый небольшой фрагмент кода, чтобы обойти эту проблему. Более подробную информацию можно найти здесь. Уловка заключается в следующем:
Затем вы используете его так:
NavigationLink(destination: LazyView( ** Put your destination view **)) { ** NavigationLink UI ** }
Надеюсь, этот небольшой обзор того, как можно реализовать MVVM с помощью SwiftUI и Combine, помог вам. Я надеюсь, что он дал вам несколько хороших идей и прояснил все ваши вопросы.
Если вам нужна дополнительная информация по конкретным темам, обращайтесь к нам. Я также всегда хочу обсудить, как улучшить, изменить или дополнить ранее обсужденные темы.