Разработка
Последний UIKit-разработчик
Ностальгический реквием по эпохе, которая отказывается умирать. Критика незавершённости SwiftUI.
На дворе 2030 год. Я сижу в кофейне в Сан-Франциско, а напротив меня сидит Тим. Ему 45, у него уже седеют виски, и он только что сказал мне, что он последний UIKit-разработчик в своей компании. Не по своей воле, все остальные много лет назад перешли на вайб-кодинг с SwiftUI. Но кто-то же должен поддерживать старое приложение.
День, когда ИИ пришёл за Тимом
На прошлой неделе компания Тима провела эксперимент. Они загрузили всю свою кодовую базу UIKit в новейшую модель ИИ. «Конвертируй это в SwiftUI», — приказали они. «Сохрани всю функциональность».
ИИ работал шесть часов. Он создал 100,000 строк кода SwiftUI. Он скомпилировался. Он даже работал. Примерно десять секунд, прежде чем завис.
ИИ преобразовал каждый UIViewController в View. Каждый шаблон делегирования в биндинг. Каждый UITableView в List. Технически всё было правильно, в лучшем виде. И всё было полностью, окончательно сломано.
Сложные распознаватели жестов? Исчезли. Пользовательские переходы? Заменены на универсальные. Оптимизация производительности, которая обеспечивала плавную работу приложения на iPhone X? Проигнорирована. Двадцать лет накопленных знаний, пограничных случаев и с трудом добытых решений: испарились.
«ИИ не понимает, почему код такой, какой он есть», — объясняет Тим. «Он видит viewDidLayoutSubviews и думает: „О, снова макет“. Он не знает, что это конкретное переопределение исправляет ошибку на iPhone 8 Plus в альбомной ориентации, когда отображается клавиатура и у пользователя включён крупный текст».
Похоронен заживо
«Все думают, что UIKit мёртв», — говорит он мне за чашкой кофе. «Но SwiftUI — это просто UIKit в красивом одеянии. Каждый NavigationStack? Это UINavigationController под ним. Каждый List? Раньше был UITableView, теперь это UICollectionView с композиционными макетами. Apple не убила UIKit. Они похоронили его заживо, и он всё ещё дышит там, выполняя всю работу».
Фреймворк, который решил всё
Давайте отдадим дань UIKit, пока он ещё технически жив. Этот фреймворк не просто создавал приложения, он создал целую индустрию. Когда в 2008 году вышла iPhone OS 2.0, UIKit уже был готов превратить телефон в платформу.
Подумайте, чего на самом деле достиг UIKit:
- Pixel-perfect макеты до того, как мы поняли, что они нам нужны;
- Прокрутка со скоростью 60 кадров в секунду на устройствах со 128 МБ оперативной памяти;
- Функции доступности, которые действительно работали;
- API анимации, которые заставили разработчиков Android плакать;
- Система иерархии представлений настолько мощная, что она до сих пор лежит в основе вашего «современного» приложения SwiftUI.
Даже сегодня, в iOS 18, цифры не лгут. Из 6800 исполняемых файлов в системе 592 используют SwiftUI.
Остальное? Полностью UIKit. Основные системные фреймворки — UIKitCore, MessageUI, ContactsUI, EventKitUI, MapKit — всё это UIKit. Когда SwiftUI нужно сделать что-то сложное, к чему он обращается? UIKit.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// This method signature built a trillion dollar ecosystem
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
// Magic happened here
return cell;
}
«Посмотрите на этот метод», — говорит Тим с неподдельным благоговением. «Это поэзия. Каждый разработчик iOS, когда-либо живший на свете, знает это наизусть. Этот код запечатлен в наших нейронах».
Архитектура, которая отказалась умирать
Вот грязный секрет, который Apple не хочет, чтобы вы знали (ну, по крайней мере, они его достаточно хорошо скрывают): SwiftUI — величайший триумф UIKit. Не его замена, а его развитие. Его окончательная форма.
Когда вы пишете этот прекрасный код SwiftUI:
NavigationStack {
List(items) { item in
Text(item.title)
}
}
Что происходит на самом деле? SwiftUI создаёт UINavigationController, оборачивает его в UIHostingController, генерирует UICollectionView (в iOS 16 для повышения производительности был переключен с UITableView) и управляет всем этим процессом за кулисами. Ваш декларативный код — это всего лишь инструкции для UIKit.
struct ContentView: View {
var body: some View {
List {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
.introspect(.list, on: .iOS(.v13, .v14, .v15)) {
print(type(of: $0)) // UITableView
}
.introspect(.list, on: .iOS(.v16, .v17, .v18, .v26)) {
print(type(of: $0)) // UICollectionView
}
}
}
Примечание: если вы хотите изучить компоненты SwiftUI и получить доступ к их компонентам UIKit, вы можете использовать библиотеку Introspect, которая просто великолепна. Она подходит как для обходных путей, так и для определения того, какие компоненты SwiftUI поддерживаются какими компонентами UIKit.
«Я постоянно использую SwiftUI Introspect», — объясняет Тим. «Вы проходите по иерархии представлений между маркерами и — бац! — вот ваш компонент UIKit. ScrollView? Это UIScrollView. TextField? UITextField. Единственные по-настоящему нативные компоненты SwiftUI — это простые: текст, изображение, кнопка. Всё сложное — это замаскированный UIKit».
И доказательство этому — приложения. Почта, Сообщения, Safari, Фото, Телефон, Карты — все приложения, которые действительно важны, которые справляются с настоящей сложностью? Всё ещё UIKit. Инженеры Apple знают: когда нужна производительность, когда нужен контроль, когда нужно, чтобы всё работало масштабируемо, нужно использовать UIKit.
Бессмертная кодовая база
В компании Тима есть The App. Вы знаете такое. Запущено в 2009 году. Полностью на Objective-C. 2 миллиона строк кода. 500 настраиваемых контроллеров представлений. Оно дает половину их дохода.
«Мы пытались мигрировать его трижды», — смеётся Тим. «Приложение Калькулятор перешло на SwiftUI в iOS 18. Приложение Пароли? Создано с нуля на SwiftUI. А наше приложение? С его настраиваемыми макетами UICollectionView, создающими каскады в стиле Pinterest? С его CALayerанимациями, синхронизированными посредством извлечения времени из кривых? С его физическими взаимодействиями с использованием UIKit Dynamics? Удачи».
ИИ согласился. Когда они передали кодовую базу в GPT-7, он проанализировал всё: распространение событий цепочки ответчиков на предмет слабого связывания, настраиваемые контроллеры представления с интерактивным закрытием, многопроходную автоматическую компоновку с динамическим переключением ограничений. И не забудьте про ввод текста и рендеринг. С пользовательскими атрибутами NSAttributedString. Вердикт?
«РЕКОМЕНДАЦИЯ: Сохранить существующую реализацию UIKit. В SwiftUI отсутствуют аналогичные возможности для: кастомных макетов коллекций, анимации свойств CALayer, физики UIKit Dynamics, управления цепочкой ответчиков и ещё примерно 47 выявленных паттернов.»
Даже ИИ понял: UIKit не сломан. Он совершенный.
Утраченное искусство ручного управления памятью
Было время, когда мы были волшебниками. Настоящими волшебниками. Мы считали ссылки в уме, балансировали каждое выделение памяти с освобождением и устраняли утечки памяти, используя только NSLog и интуицию. И использовали профилировщик, понятный только волшебникам.
- (void)setDelegate:(id<MyDelegate>)delegate {
// The ancient ritual - delegates were always 'assign' to avoid retain cycles
if (_delegate != delegate) {
_delegate = delegate; // No retain! This was the way.
}
}
- (void)dealloc {
[_myView release];
[_myArray release];
// Delegates were never released - they didn't own us
_delegate = nil;
[super dealloc];
}
«Мы жили по золотым правилам, — вспоминает Тим. — Вы владеете тем, что выделяете, создаёте или копируете. Уравновешивайте каждое владение освобождением. Никогда не освобождайте то, чем не владеете. А делегаты? Всегда назначайте, никогда не сохраняйте. Так вы избегали циклов удержания до появления слабых ссылок».
Разработанные нами тогда паттерны актуальны и сегодня. Паттерн слабого делегата? Родился из ручного управления памятью. Атрибут копирования для свойств? Защитное программирование против изменяемых строк. Даже обработка ссылочных типов в SwiftUI восходит к урокам, полученным ещё до появления ARC.
- (void)processDataWithBlock:(void (^)(NSData *))block {
// Blocks started on the stack! Had to copy them to the heap
void (^heapBlock)(NSData *) = [block copy];
[_blocks addObject:heapBlock];
[heapBlock release]; // Because copy gave us ownership
}
«NSAutoreleasePool», — вздыхает Тим. «Каждому потоку он был нужен. Фабричные методы возвращали автоматически освобожденные объекты. Это было элегантно в своей явности. Можно было точно знать, когда будет освобождена память».
Музей утраченных паттернов
У Тима на рабочем столе есть папка под названием «Музей». Она заполнена техниками UIKit, которые SwiftUI не может воспроизвести. Техник, благодаря которым было создано приложений на миллиарды (ДА, МИЛЛИАРДЫ!) долларов.
Синхронизация анимации CALayer: извлечение кривых времени из неявных анимаций для координации нескольких слоёв, что невозможно в системе анимации SwiftUI.
- (void)animateLayers {
// Begin a CATransaction to capture the implicit animation
[CATransaction begin];
[CATransaction setAnimationDuration:1.5];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
// Change the position of layerA (triggers implicit animation)
CGPoint newPosition = CGPointMake(250, 100);
self.layerA.position = newPosition;
// Commit the transaction
[CATransaction commit];
// Extract the implicit animation from layerA
CAAnimation *animation = [self.layerA animationForKey:@"position"];
if (!animation) {
// Implicit animations are not directly accessible, so we create a matching explicit animation
CABasicAnimation *syncAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
syncAnimation.fromValue = [NSValue valueWithCGPoint:self.layerB.position];
syncAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(250, 200)];
syncAnimation.duration = 1.5;
syncAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
// Apply the animation to layerB
self.layerB.position = CGPointMake(250, 200);
[self.layerB addAnimation:syncAnimation forKey:@"position"];
}
}
Кастомные макеты UICollectionView: водопады в стиле Pinterest, эффекты параллакса, моделирование физики — все это посредством ручного расчета положения каждой ячейки.
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
// 500 lines of matrix math to create magic
}
Искусство цепочки ответчиков: слабая связь между контроллерами представлений без делегатов и уведомлений.
// Send actions up the chain until someone handles it
[self.nextResponder targetForAction:@selector(handleCustomAction:)
withSender:self];
UIKit Dynamics: реальная физика для элементов пользовательского интерфейса. Гравитация, столкновения, пружины — всё встроено.
[_animator addBehavior:[[UIGravityBehavior alloc] initWithItems:@[card]]]; [_animator addBehavior:[[UICollisionBehavior alloc] initWithItems:@[card]]]; // Tinder swipe physics in 3 lines
«Это не просто старые шаблоны», — настаивает Тим. «Это решения проблем, о существовании которых SwiftUI ещё даже не знает».
Долгая игра
UIKit не умирает. Он развивается. Становится инфраструктурой. Как UNIX под macOS, как C под всем остальным, UIKit исчезает в основе самой платформы.
«Знаете, что забавно?» — спрашивает Тим. «Junior разработчики считают SwiftUI проще. Но когда что-то идёт не так, когда падает производительность, когда анимация тормозит, когда происходит утечка памяти, куда они попадают? В мой офис, где они просматривают трассировки стека UIKit, пытаясь понять, что на самом деле сделал их код SwiftUI».
Он показывает мне утечку памяти SwiftUI. Трассировка Instruments ведёт через SwiftUI, через Combine, через алгоритмы сравнения… и попадает в UIKit. Протекающий UIViewController. Классический цикл удержания. Тот же баг, который мы исправляем уже 15 лет, только с добавлением дополнительных слоёв.
Вечный фреймворк
Итак, за UIKit! Фреймворк, который создал целую индустрию. Архитектуру, которая не поддается замене. Кодовую базу, которая переживёт всех нас.
Ваши подклассы UIViewController были храмами функциональности. Иерархии UIView давали точный контроль. Ваши протоколы делегирования, возможно, были многословными, но они были честными. Ваши методы drawRect:, возможно, были опасными, но они были мощными.
И Тиму, и всем разработчикам UIKit, которые всё ещё работают и поддерживают действительно важные приложения: вы не последние из вымирающего поколения. Вы — хранители фундамента. Стражи того, что работает. Жрецы храма, на котором все остальные строят.
UIKit не умер. Он победил настолько безоговорочно, что стал невидимым. Он присутствует в каждом представлении SwiftUI, в каждом системном фреймворке, в каждой анимации, которая действительно хорошо работает. Именно 6200 двоичных файлов из 6800 обеспечивают работу iOS.
В 2040 году, когда мы будем описывать приложения для ИИ, когда никто не вспомнит, что такое протокол делегата, когда сам Swift будет всего лишь памятью, UIKit всё ещё будет существовать. Скрытый во фреймворках. Запуская анимации. Управляя памятью. Выполняя всю работу.
Потому что именно этим и занимается инфраструктура. Она растворяется в фундаменте и держит всё на своих плечах.
Конец
Это признание в любви UIKit, фреймворку, на котором я построил свою карьеру. Так же, как SwiftUI построен на основе UIkit.
Это также критика SwiftUI, который до сих пор представляет собой в основном красиво оформленную реактивную версию UIKit. Apple, безусловно, не спешит.
- (void)dealloc {
[super dealloc]
[tim release]
[story release]
}
-
Аналитика магазинов2 недели назад
Мобильный рынок Ближнего Востока: исследование Bidease и Sensor Tower выявляет драйверы роста
-
Интегрированные среды разработки3 недели назад
Chad: The Brainrot IDE — дикая среда разработки с играми и развлечениями
-
Новости4 недели назад
Видео и подкасты о мобильной разработке 2025.45
-
Новости3 недели назад
Видео и подкасты о мобильной разработке 2025.46

