Для разработки iOS-приложений можно использовать два основных фреймворка: UIKit и SwiftUI. Однако при переходе со старого инструмента на новый, многие разработчики сталкиваются с трудностями, ведь парадигмы программирования у них сильно отличаются.
Цель статьи
Помочь разработчикам приложений для iOS понять различия между императивным и декларативным подходами к программированию, а также рассмотреть плюсы и минусы фреймворков UIKit и SwiftUI. Знакомство с ними необходимо для оптимизации процесса разработки и создания продукта высокого качества.
В первой части статьи рассмотрю императивный и декларативный подходы, и это станет основой для всего последующего материала.
Во второй части расскажу о героях этой статьи — фреймворках UIKit и SwiftUI. Я покажу, какие подходы используются при разработке на каждом из них и в чём их отличия.
В третьей части разберу плюсы и минусы рассмотренных подходов, чтобы сравнить два инструмента и помочь читателю выбрать то, что лучше подходит для решения его задач.
Почему я над этим задумался?
Наша команда iOS-разработки приложения для продавцов столкнулась с проблемой отсутствия коммерческого опыта в разработке на SwiftUI и понимания двух подходов программирования: императивного и декларативного.
Темпы роста команды были очень высокими. Мы нанимали крутых инженеров, но большинство из них не сталкивались в своей работе со SwiftUI. Поэтому им приходилось переучиваться в процессе адаптации, набивать шишки на код-ревью и изучать новую технологию на ходу. База знаний о SwiftUI для погружения новых сотрудников у нас отсутствует. Эта ситуация дала понять, что большинство разработчиков сталкиваются с одной и той же проблемой: сложностью перехода с императивного на декларативный подход. Это подтолкнуло меня сначала подготовить доклад для внутреннего митапа Ozon, а позже — написать эту статью.
Содержание
- Императивный и декларативный подходы
- Устаревший UIKit 👨🦳 vs Новый SwiftUI 👦
- Преимущества и недостатки фреймворков
Императивный и декларативный подходы
Рассмотрим два подхода к программированию: императивный и декларативный.
Императивный подход основывается на определении последовательности команд и операций, которые выполняются шаг за шагом в определённом порядке. Разработчику необходимо подробно описать, как программа должна работать и какие операции должны быть выполнены, чтобы добиться нужных результатов.
UIKit — это пример императивного подхода. Фреймворк подразумевает полный контроль над состоянием интерфейса и необходимость вручную управлять каждым его элементом с помощью кода. Этот подход имеет свои преимущества в схемах, где полный контроль текущего состояния является критически важным.
Декларативный подход, в свою очередь, сфокусирован на том, чтобы описывать, что программа должна делать, а не как это должно быть выполнено. Разработчик описывает желаемый результат, а система сама определяет, как его получить.
SwiftUI - это пример декларативного подхода. Он позволяет разработчикам указывать, что они хотят отображать на экране, и система на основании этого описания сама определяет, как будет выглядеть интерфейс.
Благодаря такому подходу, разработчики могут писать менее сложный код. Система берет на себя множество задач, связанных с управлением состоянием. Ранее приходилось выполнять эти задачи вручную при императивном подходе.
Для того, чтобы более четко понимать, о каком состоянии идет речь, уточню- это состояние пользовательского интерфейса. Разработчик может описать, как должен выглядеть интерфейс при различных сценариях, например, когда пользователь что-то вводит или прокручивает контент.
Таким образом, декларативный подход в SwiftUI позволяет разработчикам сосредоточиться на описании того, что нужно получить в итоге, вместо того, чтобы писать множество манипуляций для управления состоянием пользовательского интерфейса.
Давайте представим, что у нас есть компания. У неё есть руководитель, пожилой человек, который символизирует императивную часть компании, аналогичную UIKit. У этого руководителя методы управления довольно архаичные. Он настаивает на том, чтобы все его инструкции выполнялись строго в том порядке, который он установил, и в точности так, как он их сформулировал. В противном случае, по его мнению, компания не сможет добиться нужного результата. На схеме выше видно, что от руководителя спускается ряд команд, а для достижения конечного результата нужно совершить несколько действий.
С другой стороны, у нас есть декларативный стартап, которым руководит парнишка, он же Mr SwiftUI. У него не очень много опыта, но большие амбиции и стремления, он полностью доверяет своим подчинённым и при распределении задач полагается на их опыт и экспертизу, поэтому просто описывает видение конечной реализации и делегирует решение коллегам.
Примеры кода
В этих примерах мы решаем одну и ту же задачу — вычисление суммы квадратов чисел в массиве — но делаем это по-разному: с помощью императивного и декларативного подходов.
В первом примере мы используем цикл for, чтобы последовательно перебрать элементы массива, и переменную sum, чтобы хранить сумму. Таким образом, мы описываем последовательность шагов, необходимых для решения задачи.
Во втором примере мы также определяем функцию для нахождения суммы квадратов чисел в массиве. Однако вместо последовательного описания шагов мы используем функциональные методы map и reduce. Метод map преобразует каждый элемент массива в его квадрат, а метод reduce находит сумму преобразованных элементов. То есть мы описываем, что нужно сделать, а не как.
Важно знать об особенностях каждого подхода, чтобы выбирать оптимальные инструменты в зависимости от поставленных задачи и требований проекта.
Важно понимать, что выбор подхода зависит от поставленных задач и требований проекта. Например, для написания алгоритмов с последовательным выполнением операций предпочтительным может быть императивный подход, а для описания пользовательского интерфейса декларативный подход может оказаться более удобным.
Устаревающий UIKit 👨🦳 vs Новый SwiftUI 👦
В этом разделе мы рассмотрим два фреймворка для разработки пользовательских интерфейсов для iOS: устаревающий UIKit и новый SwiftUI. Мы разберём их особенности и отличия, а также рассмотрим примеры кода на UIKit и SwiftUI, и какой код писать точно не нужно.
UIKit — это фреймворк, который применяется для создания пользовательского интерфейса в iOS-приложениях с 2007 года. Он использует императивный подход к разработке, на который опираются программисты, описывая на Swift и Objective-C, как создавать и настраивать элементы интерфейса. Вы определяете все свойства каждого элемента: положение на экране, размеры, цвета, шрифты и т. д. UIKit предоставляет великолепный инструментарий, однако требует высокого уровня квалификации для работы с ним.
SwiftUI Apple представила в 2019 году. Это фреймворк, который реализует декларативный подход к разработке пользовательского интерфейса. Он даёт возможность описывать элементы интерфейса, используя концепцию зависимостей данных между ними, а не процесс их создания и настройку. Это означает, что каждый элемент интерфейса зависит от данных, которые ему передаются. Если эти данные изменяются, то соответствующие элементы интерфейса автоматически обновляются, что является однонаправленным подходом в программировании. Это означает, что изменения в модели данных автоматически обновляют представление, но обратного не происходит.
Чтобы лучше понимать особенности SwiftUI, полезно знать о связке с реактивным программированием и библиотекой Combine.
Реактивное программирование — это концепция, в которой данные представляются в виде потоков (streams), автоматически обновляющихся при изменении состояния представления. А библиотека Combine позволяет использовать реактивный подход в SwiftUI, упрощая работу с потоками данных.
Действие -> Состояние -> Отображение
SwiftUI — это новый инструмент для разработки пользовательского интерфейса iOS-приложений, который быстро набирает популярность у разработчиков. Он прост в использовании и гибок, что позволяет писать код значительно быстрее, чем со старым UIKit.
Однако многие разработчики всё ещё не уверены в своих силах и предпочитают использовать привычные методы. Но SwiftUI даёт возможность значительно упростить процесс разработки и уменьшить количество ошибок, а это важные факторы для любого проекта и весомые аргументы в его пользу.
SwiftUI не является зависимым звеном в цепочке и не заменяет UIKit. Это новый инструмент, который помогает разработчикам создавать пользовательские интерфейсы быстро и легко.
Давайте рассмотрим процесс создания кнопки в обоих фреймворках.
Императивный подход на UIKit
Декларативный подход на SwiftUI
Оба этих примера демонстрируют создание кнопки, нажатие на которую выводит сообщение “Button tapped” на консоль. Однако декларативный подход на SwiftUI гораздо более лаконичный и понятный. Вместо того чтобы настраивать каждый аспект кнопки отдельно, мы описываем кнопку в терминах её желаемых свойств — и SwiftUI самостоятельно обеспечивает реализацию. Это может существенно ускорить процесс разработки и уменьшить количество ошибок.
Кроме того, SwiftUI в связке с Combine позволяет нам использовать многие удобные функции, такие как, например, автоматические обновления интерфейса при изменении состояния, что делает создание пользовательских интерфейсов ещё более простым и эффективным.
Теперь рассмотрим процесс создания таблиц в обоих фреймворках.
TableView на UIKit
По коду понятно, что реализовать таблицу на UIKit довольно сложно.
Таблица на SwiftUI
А вот реализация таблицы на SwiftUI не требует стольких усилий — код выглядит куда проще.
Хороший пример с плохим кодом
Данный код на SwiftUI нарушает структуру View, поскольку в нём нет её объявления и функции body, которая должна содержать описание интерфейса. Переменные label и button объявлены вне функции body, что может вызвать ошибки при компиляции. Кроме того, использование модификаторов неоптимально: переменная label переопределяется три раза, что затрудняет чтение кода и может привести к утере значений его свойств. Помимо этого, кнопка не связана с функцией body и не обрабатывает события. Подобный код будет сложно поддерживать, он требует много времени на написание и ревью. Он не будет соответствовать стандартам качества кода и не будет принят инженерами.
Если помните, в начале я писал, что мы нанимаем крутых разработчиков, но они приходят без опыта работы со SwiftUI. Это не является блокером, и мы знаем, что через какое-то время они вольются в процесс и будут писать код по стайлгайдам. Но на первых порах мы можем наблюдать попытки сваять похожие конструкции.
Итак, SwiftUI — это новый инструмент для разработки пользовательского интерфейса с низким порогом входа и декларативным подходом к программированию, который упрощает создание пользовательского интерфейса. Однако при наличии многолетнего опыта работы с UIKit многие разработчики предпочитают продолжать работать с ним. Но ведь мы должны быть готовы меняться и адаптироваться к новым технологиям, чтобы оставаться на плаву в быстро меняющемся мире iOS-разработки.
Меняйся или сдохни
Мы уже познакомились с обоими фреймворками, рассмотрели примеры кода, выяснили, как писали код раньше, во времена динозавров, а как это делают с приходом новых технологий, и, конечно же, увидели, как писать код на SwiftUI не стоит, на приближенном к реальности примерe.
Кажется, мы уже немного гуру и правильно понимаем различия между подходами и фреймворками, понимаем, как нам следует писать трушный декларативный пользовательский интерфейс, а как этого лучше не делать. Ну а чтобы завершить эту тему и получить ещё больше экспертизы, дальше мы рассмотрим различные характеристики каждого фреймворка.
Преимущества и недостатки фреймворков
Некоторые инженеры, которые приходят к нам в Ozon Tech, считают, что SwiftUI — полная фигня и им не стоит пользоваться, тем более в коммерческой разработке. Другие же уверены, что UIKit — это безнадёжно устаревшая технология динозавров. И все они правы! Но не совсем.
Мы переходим к основной части моей работы. Я поговорил с большим количеством экспертов, провёл масштабное исследование и выписал неопровержимые плюсы и минусы SwiftUI и UIKit. Давайте на них посмотрим:
Посмеялись — и хватит 🤪
Как вы могли догадаться, табличка шуточная. А теперь предлагаю взглянуть на реальную:
Скорость вёрстки: SwiftUI +, UIKit -
При разработке пользовательского интерфейса с использованием SwiftUI, процесс верстки значительно упрощается. Широкий набор встроенных компонентов позволяет создавать интерфейс, используя декларативный подход, что значительно сокращает объем кода и уменьшает вероятность ошибок.
В SwiftUI не нужно задавать точные координаты каждому элементу интерфейса, описывая их в коде. Вместо этого, разработчик может использовать макеты и стеки для автоматического позиционирования элементов. Это дает возможность быстро создавать пользовательский интерфейс без необходимости тратить время на выравнивание каждого элемента вручную.
Также, SwiftUI предлагает удобный подход к созданию списков и таблиц, используя компоненты List и Form. Они автоматически настраиваются в зависимости от данных, что значительно упрощает процесс создания списков и таблиц.
В отличие от SwiftUI, UIKit использует императивный подход, где разработчику необходимо явно определять каждый элемент пользовательского интерфейса. Это может привести к дополнительным затратам времени, так как разработчику приходится задавать точные координаты каждого элемента вручную.
Кроме того, в UIKit требуется больше кода, чтобы создавать интерфейс, что может замедлить процесс разработки и усложнить поддержку кода в будущем.
Таким образом, использование SwiftUI может значительно повысить скорость верстки пользовательского интерфейса, благодаря использованию декларативного подхода и упрощенной структуре кода. Однако, следует учитывать, что для создания сложных пользовательских интерфейсов могут потребоваться дополнительные инструменты и подходы.
Совместимость с легаси-кодом: SwiftUI -, UIKit +
UIKit появился раньше, чем SwiftUI, он работает с Objective-C и Swift, что обеспечивает совместимость с готовым к использованию легаси-кодом.
Особенностью SwiftUI являются жирные структуры. Это структуры, которые содержат много переменных и методов. Каждый элемент пользовательского интерфейса — кнопка, текстовое поле или изображение — в SwiftUI представлен в виде отдельной структуры.
Однако, в отличие от Swift, Objective-C не поддерживает жирные структуры. Следовательно, нет способа написать пользовательский интерфейс с помощью SwiftUI и импортировать его в приложение, написанное на Objective-C. Это означает, что разработчики, которые хотят использовать SwiftUI, должны перейти на Swift и создать новое приложение с нуля, если хотят использовать весь потенциал фреймворка.
Дебаггинг: SwiftUI -, UIKit +
При разработке мобильных приложений, одной из важных задач является дебаггинг. Дебаггинг - это процесс поиска и устранения ошибок в исходном коде приложения.
Одним из выборов разработчиков является выбор между двумя фреймворками: SwiftUI и UIKit. Каждый из них имеет свои преимущества и недостатки при дебаггинге.
UIKit имеет долгую историю и широкое сообщество разработчиков, что делает дебаггинг на этой платформе более простым. UIKit имеет хорошо разработанные инструменты дебаггинга, такие как инструменты Xcode, которые позволяют быстро находить и исправлять ошибки.
С другой стороны, SwiftUI является новым фреймворком.. У него есть свои преимущества, такие как более простой синтаксис и быстрота верстки. Однако, это также делает дебаггинг на SwiftUI менее эффективным, так как инструменты для дебаггинга еще не так хорошо развиты как для UIKit.
SwiftUI имеет свой уникальный подход к жизненному циклу представлений, что может вызывать проблемы в процессе дебаггинга. Например, в SwiftUI может возникнуть проблема с обновлением вида, когда изменение в одном представлении может повлиять на другие представления и вызвать ошибки. Кроме того, SwiftUI имеет ограничения в работе с различными типами данных, которые могут приводить к ошибкам в процессе дебаггинга.
Мультиплатформенность: SwiftUI +, UIKit -
SwiftUI был создан как фреймворк для мультиплатформенной разработки приложений, что позволяет использовать единый код для создания приложений для разных платформ: iOS, macOS, watchOS и tvOS. UIKit ориентирован на создание приложений только для iOS.
Поддержка сообщества: SwiftUI -, UIKit +
UIKit является «взрослым» общепризнанным фреймворком, который объединяет большое сообщество разработчиков и множество инструментов и библиотек для его использования. SwiftUI только появился и его сообщество пока ещё не такое развитое и большое, как у UIKit.
Количество кода: SwiftUI +, UIKit -
SwiftUI благодаря своему декларативному подходу позволяет уменьшить количество кода, необходимого для создания пользовательского интерфейса, и сделать его более читаемым и понятным. UIKit работает в императивном стиле и требует больше кода для достижения тех же результатов.
Совместимость с Modern Concurrency: SwiftUI +, UIKit -
SwiftUI представляет собой фреймворк, который полностью поддерживает Modern Concurrency с помощью новых Swift API, таких как async/await и Combine. UIKit же как старший фреймворк может не всегда соответствовать современным требованиям к асинхронному программированию и требовать дополнительных усилий для написания асинхронного кода.
Поддержка версий: SwiftUI -, UIKit +
SwiftUI был представлен в 2019 году вместе с iOS 13, поэтому поддерживает только устройства на ней и более новых версиях. Это означает, что разработка приложения с использованием SwiftUI может стать проблематичной, если требуется поддержка более старых версий iOS.
UIKit в дополнение к поддержке iOS 13 и более новых версий поддерживает и более ранние версии iOS. Это означает, что с ним разработчики могут создавать приложения, которые будут работать на более старых устройствах и версиях платформы iOS.
Быстродействие: SwiftUI +, UIKit -
SwiftUI предоставляет более быстрое и эффективное развитие своего инструментария по сравнению с UIKit. Он ориентирован на использование структур, тогда как UIKit использует классы для создания пользовательского интерфейса. Использование структур делает код на SwiftUI более лёгким, читабельным и удобным в работе. Структуры в Swift хранятся в стеке, а не в куче, как классы, — это обеспечивает более эффективное использование памяти (более быстрое выделение и освобождение).
Также структуры в Swift не требуют сборки мусора, в отличие от классов, что способствует более быстрой работе кода в структурах. Использование классов в UIKit может утяжелить код, сделать его менее понятным и более сложным в обслуживании. Однако использование классов может быть предпочтительным в случаях, когда необходимо использовать наследование и другие концепции, которые не поддерживаются структурами.
Использование структур в SwiftUI может значительно ускорить разработку и обеспечить более эффективное использование памяти, а это может повысить быстродействие приложений.
Одной из ключевых особенностей, способствующей быстродействию SwiftUI, является его декларативный подход к созданию пользовательского интерфейса. Вместо того чтобы явно обновлять каждый элемент интерфейса вручную, SwiftUI использует концепцию "объявления состояния" (state declaration), где вы определяете желаемое состояние интерфейса, а фреймворк автоматически обрабатывает обновления и перерисовку только тех частей, которые изменились.
Такой подход позволяет SwiftUI выполнять оптимизацию и сокращать ненужные операции перерисовки, что в результате повышает быстродействие. Фреймворк также использует различные стратегии кэширования и инкрементальной перерисовки, чтобы минимизировать количество работы, которое необходимо выполнить при изменении интерфейса.
Благодаря использованию новых технологий и оптимизаций, SwiftUI также может использовать преимущества аппаратного ускорения, такого как Metal и Core Animation, для повышения производительности графики и анимаций.
В целом, SwiftUI стремится обеспечить более высокую производительность и быстродействие по сравнению с UIKit, упрощая разработку, оптимизируя перерисовку и используя современные технологии для ускорения работы с графикой и анимациями.
Все эти оптимизации и подходы к разработке в SwiftUI в целом способствуют более быстрому развитию проектов, более эффективному использованию ресурсов и повышению производительности пользовательского интерфейса.
Однако следует отметить, что факторы, такие как сложность проекта и качество кода, также могут влиять на общую производительность приложения.
Кто же победил?
Оба рассмотренных фреймворка имеют свои преимущества и недостатки, и разработчики должны выбирать инструмент в соответствии с конкретным приложением и его требованиями.
В заключительном разделе мы рассмотрели плюсы и минусы фреймворков, и это даёт возможность провести параллели между совершенно разными инструментами и углубить свою экспертизу.
При работе с версткой пользовательских интерфейсов, мы можем использовать фреймворки UIKit или SwiftUI. Однако, важно понимать, что каждый из них имеет свои особенности и специфику работы.
Получилось осветить все аспекты, которые помогают нам в решении первоначальной проблемы, с отсутствием экспертизы в разработке на новом фреймворке и в понимании парадигм, по которым работают основные инструменты для работы с пользовательским интерфейсом..
Сейчас мы имеем представление о ключевых парадигмах в iOS-разработке, так как погрузились в академические определения и рассмотрели примеры кода, разобрались в особенностях SwiftUI и UIKit, увидели отличия в количестве кода и код, который лучше не писать, и, конечно же, узнали о плюсах и минусах двух фреймворков, что внесло ясность в общую картину и понимание положения этих инструментов в мире iOS-инженера.
В этой статье мы рассмотрели:
- особенности императивного и декларативного подходов программирования;
- особенности фреймворков UIKit 👨🦳 и SwiftUI 👦;
- плюсы и минусы фреймворков UIKit 👨🦳 и SwiftUI 👦.
Несмотря на то, что дедуля UIKit не использует декларативный подход, он является очень мощным и гибким инструментом для создания пользовательского интерфейса iOS-приложений, и не стоит списывать его со счетов. Но сейчас у него есть серьёзный конкурент в виде относительно нового фреймворка, который впечатляет своими фичами, взять хотя бы сниженный порог входа в разработку. Может повториться история Objective-С и Swift, ведь реальность такова, что Apple движется в сторону технологических улучшений своих инструментов для разработки, и увеличение популярности SwiftUI и переход на его сторону многочисленного комьюнити — вопрос времени.
Полезные материалы
1. Фреймворк Carbon, который даёт возможность потрогать декларативный подход в UIKit. Это библиотека для создания пользовательских интерфейсов на основе компонентов UITableView и UICollectionView, вдохновлённая SwiftUI и React.
2. Реактивное программирование с Combine: тут и тут
3. Доклад Руслана Кавецкого из Agora «Избавляемся от старья и переходим на SwiftUI». 4. Статья «SwiftUI по полочкам».
*Благодарю за помощь в написании статьи моих коллег и друзей: