SwiftUI или UIKit: что выбрать для нового iOS-приложения
Практика3 минРедакция Innovialabs
Коротко
Для нового iOS-приложения в большинстве случаев стоит начинать со SwiftUI и подключать UIKit точечно там, где не хватает контроля; существующий UIKit-проект не переписывают, а добавляют в него новые экраны на SwiftUI.
Короткий ответ
Для нового приложения разумный выбор по умолчанию — SwiftUI как основа и UIKit точечно, там где SwiftUI не хватает возможностей или контроля.
UIKit как основной фреймворк оправдан, если:
- нужно поддерживать старые версии iOS;
- интерфейс построен на очень нестандартных компонентах: сложные текстовые редакторы, нетипичные переходы, тяжёлые списки с собственной раскладкой;
- команда сильна в UIKit, а сроки не позволяют переучиваться.
Если приложение уже написано на UIKit, его не переписывают целиком: новые экраны делают на SwiftUI и встраивают в существующий код.
Декларативный и императивный подход
В UIKit вы создаёте элементы, храните на них ссылки и вручную обновляете их при каждом изменении данных:
final class CounterViewController: UIViewController {
private var count = 0
private let button = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
view.addSubview(button)
updateTitle()
// плюс ограничения Auto Layout для кнопки
}
@objc private func didTap() {
count += 1
updateTitle()
}
private func updateTitle() {
button.setTitle("Нажато: \(count)", for: .normal)
}
}
В SwiftUI вы описываете, как интерфейс выглядит при данном состоянии, а обновление берёт на себя фреймворк:
struct CounterView: View {
@State private var count = 0
var body: some View {
Button("Нажато: \(count)") {
count += 1
}
}
}
Кода меньше, а рассинхронизация данных и интерфейса случается реже. Цена — меньше прямого контроля: иногда сложно понять, почему представление перерисовалось или повело себя не так, как ожидалось.
Сравнение по ключевым критериям
| Критерий | SwiftUI | UIKit |
|---|---|---|
| Подход | декларативный | императивный |
| Объём кода | меньше | больше |
| Зрелость | активно развивается, поведение может меняться между версиями iOS | многолетний, предсказуемый, много готовых решений |
| Тонкий контроль | ограничен | полный |
| Платформы Apple | общий код для iOS, iPadOS, macOS, watchOS с адаптацией | iOS и iPadOS |
| Зависимость от версии iOS | сильная: новые API привязаны к новым iOS | слабая |
Минимальная версия iOS решает многое
Возможности SwiftUI определяются версией iOS на устройстве пользователя, а не версией Xcode. Новые API появляются вместе с новыми iOS и на старые версии не переносятся. Поэтому:
- чем выше минимальная версия iOS, тем меньше обходных решений и тем приятнее работать со SwiftUI;
- при низкой минимальной версии многие удобные инструменты придётся оборачивать в проверки
if #availableили заменять UIKit-кодом.
Пользователи iPhone обычно обновляются быстро, но старые модели упираются в предел поддержки, а в некоторых регионах популярны подержанные устройства. Смотрите на аналитику своей аудитории, прежде чем фиксировать минимальную версию.
Как SwiftUI и UIKit работают вместе
Смешивать фреймворки — нормальная, официально поддерживаемая практика:
- UIKit внутри SwiftUI — через
UIViewRepresentableиUIViewControllerRepresentable. Так встраивают камеру, сложные текстовые поля, представления сторонних SDK. - SwiftUI внутри UIKit — через
UIHostingController:
let profile = UIHostingController(rootView: ProfileView(user: user))
navigationController?.pushViewController(profile, animated: true)
Как мигрировать UIKit-проект
- Не переписывайте всё сразу. Полный рефакторинг — это месяцы без новых функций.
- Новые экраны делайте на SwiftUI и показывайте через
UIHostingController. Навигацию поначалу оставьте в UIKit. - Переносите снизу вверх: ячейки, мелкие компоненты, экраны настроек, потом сложные экраны.
- Отделите бизнес-логику от интерфейса: модели и сервисы не должны зависеть от того, какой фреймворк рисует экран.
- Переводите навигацию на SwiftUI последней, когда большая часть экранов уже перенесена.
Частые ошибки
- SwiftUI при слишком низкой минимальной iOS — обходные решения появляются на каждом экране.
- Борьба со SwiftUI за пиксель вместо того, чтобы обернуть готовый UIKit-компонент.
- Бизнес-логика внутри View — её становится трудно тестировать и переносить.
- Переписывание рабочего приложения только ради моды.