
Главное
• Чистый SwiftUI редко становится правильным выбором для видеоконференций. Самый быстрый путь к работающему приложению в 2026 году — гибрид: SwiftUI для обрамления и элементов управления, UIKit для видео-полотна и рендереров.
• SwiftUI потребляет примерно на 13% больше памяти и на 5–10% больше CPU, чем UIKit, на сопоставимых сетках видео. Это начинает играть роль, когда вы переходите рубеж в ~12 одновременных плиток или ориентируетесь на старые устройства.
• Видео-поверхность должна быть на UIKit — AVSampleBufferDisplayLayer, Metal-вью и рендереры WebRTC являются подклассами UIView, поэтому вы оборачиваете их в UIViewRepresentable и защищаете от пересоздания при каждой перерисовке.
• Баги в цикле состояния убивают больше видео-приложений, чем низкий FPS. Никогда не храните кадры в @State; используйте @Observable (iOS 17+) с локальным чтением и пакетируйте обновления через Combine.
• Фора Софт выпустила 625+ продуктов реального времени с видео. Если вам нужен консультационный созвон по выбору SwiftUI или UIKit для вашего приложения, мы пришлём матрицу решений в течение 48 часов после первого 30-минутного звонка.
Почему этот playbook написала Фора Софт
Мы создаём продукты с видео реального времени на iOS с тех пор, как на платформе появились первые сборки WebRTC. За 21 год мы выпустили более 625 видео- и стриминговых продуктов: от приложений для звонков один на один вроде Speakk до классных платформ с тысячами одновременных плиток вроде BrainCert, и от социальных видеосетей вроде ChillChat до облачных переговорных вроде ProVideoMeeting. Когда в 2019 году вышел SwiftUI, мы попытались перейти на него агрессивно — и на собственном опыте поняли, где он силён, где ломается под нагрузкой реального времени, а где UIKit по-прежнему отрабатывает свой хлеб.
Этот playbook — то, что мы рассказали бы основателю, CTO или руководителю инженерной команды на 30-минутном звонке. Это не общее SwiftUI — будущее
; это прагматичный гид по решениям, основанный на приложениях, которые реально пережили продакшен: реальные устройства, реальные сети, реальные бюджеты и реальные пользователи, которые не терпят зависшую плитку во время продающей встречи или медицинской консультации. Каждую рекомендацию мы подкрепляем цифрами, SDK, которые используем каждый день, и ошибками, которые мы совершили, чтобы вам не пришлось их повторять.
Если вам нужно второе мнение по выбору фреймворка или оценка по вашему видео-приложению, наш процесс Agent Engineering превращает 30-минутный звонок в пронумерованный план за дни, а не недели. Свяжитесь с нами и принесите свой самый сложный крайний случай.
Не можете решить, что выбрать — SwiftUI или UIKit для видео-приложения?
Расскажите про целевые устройства, количество участников и срок запуска. Мы предложим гибридную архитектуру и реалистичные сроки.
Вердикт — на чём реально выпускают видеозвонки в 2026 году
Короткий ответ: каждое серьёзное iOS-приложение для видеоконференций, которое мы видели в продакшене в 2026 году, либо построено только на UIKit, либо является гибридом SwiftUI и UIKit. Приложения для звонков на чистом SwiftUI существуют, но это прототипы, внутренние инструменты или демо для звонков один на один — не платформы, которые обслуживают тысячи одновременных плиток на стабильной частоте кадров. Zoom, Microsoft Teams и Google Meet под капотом по-прежнему рендерят видео-поверхности на UIKit. Сама Apple выпускает FaceTime на внутренних фреймворках, которые ближе к UIKit, чем к SwiftUI.
Причина архитектурная, а не модная: рендеринг видео в iOS происходит на AVSampleBufferDisplayLayer, Metal-слоях или WebRTC-вью RTCMTLVideoView — и всё это подклассы UIView. SwiftUI предоставляет их через UIViewRepresentable, но каждый такой мост — это шов, который может пересоздать рендерер, отбросить кадры или вступить в конфликт с механизмом сравнения SwiftUI. В чат-приложении этот шов незаметен. В классе на 20 плиток это разница между возвратом денег и потерей корпоративного клиента.
Это не делает SwiftUI плохим выбором. Это делает его правильным выбором для 70% интерфейса, которые не являются живым видео — чат, списки участников, настройки, онбординг, экраны, связанные с CallKit, прелобби. Наше правило: SwiftUI снаружи встречи, UIKit (или обёрнутый UIKit) внутри неё. Это правило позволяет вам двигаться быстро и при этом сохранять плавность встреч.
Выбирайте гибрид SwiftUI и UIKit, когда: вам нужно выпустить быстро, в ваших встречах 2–24 одновременных видеоплитки, а команда готова написать хотя бы одну обёртку UIViewRepresentable вокруг видео-рендерера.
Где SwiftUI хорош, а где ломается
SwiftUI зарабатывает себе репутацию на всём, что не является высокочастотной работой с пикселями. Онбординг, логин, экраны панели управления, списки чатов, панели участников, настройки, выбор устройств, субтитры, реакции, оценки в конце звонка — везде здесь декларативный синтаксис SwiftUI сокращает время разработки на 30–40% и делает предпросмотр повседневным инструментом. @Observable (iOS 17), SF Symbols, встроенный тёмный режим и Dynamic Type — это выигрыш в продуктивности, который ваша команда почувствует уже в первую неделю.
SwiftUI ломается под нагрузкой видео на пути высокочастотных обновлений. Если вы храните покадровые или попартисипантные битмапы в @State, вы запустите каскадные проходы сравнения и увидите, как насыщается главный поток. Если вы наивно поместите UIViewRepresentable внутрь родительского представления, которое часто перерисовывается, ваш рендерер будет уничтожаться и пересоздаваться при каждой перерисовке — пользователи увидят мерцание, чёрные кадры или откат на одно аудио. Мы отлаживали такие баги на выпущенных приложениях, и эти проблемы никогда не появляются в первую неделю
проекта; они всплывают через три месяца, после того как количество участников переваливает за 8 или 12.
Второй режим отказа — платформенные функции, которые ещё не догнали. AVPictureInPictureController, кастомная блокировка ориентации, кастомный UI CallKit, тонкая разводка жестов между pinch-to-zoom на плитке и swipe-to-dismiss на родителе — всё это до сих пор требует UIKit как запасного люка. SwiftUI с каждым годом подбирается ближе, но для продакшен-видео-приложения вы всё равно коснётесь UIKit хотя бы в одной из этих точек.
Где SwiftUI отрабатывает свой хлеб
1. Обрамление приложения. Стеки навигации, табы, модальные шторки и кнопки тулбара превращаются в однострочники. NavigationSplitView и NavigationStack справляются с раскладками iPad и Mac Catalyst без отдельных сторибордов.
2. Чат и списки участников. Списки с ForEach по модели участника — чистые, быстрые и хорошо сравниваются. Перетаскивание для сортировки, swipe-действия, поиск и pull-to-refresh достаются бесплатно.
3. Реакции, оверлеи и аннотации. Анимированные оверлеи поверх видеоплитки — emoji-реакции, бейджи поднятой руки, ярлычки с именами — это места, где блистают .animation и matchedGeometryEffect.
4. Предпросмотр и обзоры дизайна. С макросами #Preview ваш дизайнер может прокликать конечный автомат звонка, не собирая и не запуская приложение. Только это экономит неделю за квартал на команде из пяти человек.
5. Доступность. VoiceOver, Dynamic Type и уменьшенная анимация работают из коробки. UIKit требует дополнительного кода под каждое; SwiftUI получает их практически бесплатно.
Где SwiftUI ломается под нагрузкой видео
1. Циклы состояния на пути видео. Хранение CVPixelBuffer или CMSampleBuffer в @State вызывает полную перерисовку на каждый кадр. Даже плитка с 15 fps может насытить главный поток. Решение — держать кадры полностью вне SwiftUI: видео-слой рендерится сам, а SwiftUI владеет только раскладкой.
2. Пересоздание UIViewRepresentable. Каждая перерисовка родителя может заново вызвать makeUIView и снести нижележащий слой. С этим борются стабильной идентичностью (.id(participant.id)), equatable-моделями и тем, что выносят representable за пределы пути чтения состояния родителя.
3. LazyVGrid с множеством плиток. Для более чем ~20 участников LazyVGrid лениво создаёт и уничтожает плитки по мере прокрутки. С прикреплённым живым видео это означает, что видео останавливается и стартует на краях прокрутки. UICollectionView с предзагрузкой справляется с этим аккуратнее.
4. Конфликты жестов. Pinch-to-zoom на плитке, drag-to-dismiss, tap-to-focus — модификаторы жестов SwiftUI сталкиваются с распознавателями жестов UIKit внутри вашего рендерера. Координаторы и simultaneousGesture решают большинство случаев, но каждый из них — отдельная сессия отладки.
5. Управление ориентацией на одном экране. Зафиксировать один экран в альбомной ориентации, пока остальная часть приложения остаётся в портретной, по-прежнему требует подкласса UIHostingController и переопределений. Чистый SwiftUI в iOS 17/18 не делает это чисто.
Где UIKit по-прежнему выигрывает на видео-поверхностях
UIKit на десять лет зрелее SwiftUI, и нигде это не видно так явно, как внутри звонка. Каждый коммерческий видео-SDK — LiveKit, Agora, Twilio, Zoom, Daily, 100ms, Vonage — поставляет свой рендерер как UIView. Каждый фреймворк Apple на пути видео — AVFoundation, VideoToolbox, Metal, AVKit, UI провайдера CallKit — ориентирован на UIKit в первую очередь. Когда во встрече что-то идёт не так, ответы на Stack Overflow, примеры с WWDC и инженерные блоги, которые вы будете искать, — все будут на UIKit.
Производительность — вторая причина. Замеренные бенчмарки, опубликованные в 2025 году, показывают, что UIKit использует примерно на 13% меньше памяти и на 5–10% меньше CPU, чем SwiftUI, на сопоставимых сетках участников. На звонке один на один разница незаметна. На 12-человечной встрече в классе на iPhone 12 это разница между стабильными 30 fps и тепловым тротлингом до 15 fps через 20 минут. На старых устройствах (iPhone X, iPad mini 5) разрыв расширяется до 20% и больше.
UIKit также даёт вам прямые и предсказуемые жизненные циклы. viewDidLoad, viewWillAppear и viewDidDisappear дают вам точные точки входа, чтобы запустить или остановить камеру, прикрепить удалённый рендерер или освободить MTKView. .onAppear и .task в SwiftUI близки, но есть крайние случаи (переключение табов, закрытие модалок, уход в фон), где они срабатывают непредсказуемо и заставляют вас бороться с дублирующимися треками или брошенными сессиями.
Выбирайте UIKit, когда: вы рендерите живое видео на 24+ fps, ваша встреча должна поддерживать 12+ одновременных плиток на iPhone 13 или старше, либо вы интегрируетесь напрямую с AVCaptureSession, Metal или низкоуровневым видео-API WebRTC.
Гибридный шаблон, который реально используют выпущенные приложения
Примерно 70% профессиональных iOS-команд, выпускающих видео-продукты в 2026 году, используют ту или иную версию одного и того же шаблона: каркас приложения — SwiftUI, экран звонка — SwiftUI-контейнер, а сами видеоплитки — рендереры UIKit, обёрнутые в UIViewRepresentable. Состояние течёт через общую для обоих слоёв модель звонка с @Observable. Результат сочетает скорость разработки SwiftUI на 70% поверхности со стабильностью UIKit на тех 30%, которые важнее всего.
Архитектурное правило, которое мы даём каждой команде, — простое трёхслойное разделение. Слой первый — чистая Swift-модель домена звонка: комната, локальный участник, удалённые участники, треки, состояние медиа — без импортов UIKit или SwiftUI. Слой второй — @Observable-фасад, который читает интерфейс. Слой третий — интерфейс, где SwiftUI владеет раскладкой, а UIKit владеет пикселями. Если держать модель домена свободной от фреймворков, её можно юнит-тестировать, переиспользовать на macOS через Catalyst и заменить стек интерфейса позже, если Apple изменит правила игры.
Обёртка UIViewRepresentable вокруг видео-рендерера должна быть минимальной. Создавайте вью один раз, задавайте идентификатор участника через updateUIView, никогда не пересоздавайте Metal-слой и используйте EquatableView или явный .id(), чтобы SwiftUI не сносил её. Помещайте representable в LazyVGrid только если количество участников ниже ~20, и используйте UICollectionView для всего, что больше.
// Минимальный безопасный UIViewRepresentable для плитки WebRTC
struct VideoTile: UIViewRepresentable, Equatable {
let participantID: String
let renderer: RTCVideoRenderer // кэшируется снаружи через VM
func makeUIView(context: Context) -> RTCMTLVideoView {
let view = RTCMTLVideoView()
view.videoContentMode = .scaleAspectFill
return view
}
func updateUIView(_ view: RTCMTLVideoView, context: Context) {
// Подключаем трек только один раз на участника; CallModel владеет кэшем
CallModel.shared.attach(participantID: participantID, to: view)
}
// Equatable не даёт SwiftUI заново вызывать representable
static func == (lhs: VideoTile, rhs: VideoTile) -> Bool {
lhs.participantID == rhs.participantID
}
}
Бенчмарки производительности бок о бок
Цифры ниже агрегируют опубликованные бенчмарки и наши внутренние замеры на сетках из 4, 12 и 24 плиток, на которых работает WebRTC SFU на iPhone 12, 14 Pro и 15 Pro. Абсолютные значения зависят от разрешения плитки и кодека, поэтому относитесь к ним как к относительным дельтам. Вывод устойчивый: SwiftUI достаточно дёшев для обрамления и маленьких встреч, заметен на среднем масштабе и становится обузой на большом масштабе без UIKit-обходов.
| Сценарий | Метрика | Чистый UIKit | Гибрид | Чистый SwiftUI |
|---|---|---|---|---|
| 4 плитки, iPhone 14 Pro | Устойчивый FPS | 30 | 30 | 29–30 |
| 4 плитки, iPhone 14 Pro | Пиковая память | ~92 МБ | ~105 МБ | ~140 МБ |
| 12 плиток, iPhone 12 | Устойчивый FPS | 28–30 | 26–30 | 18–24 (тротлинг) |
| 12 плиток, iPhone 12 | CPU на 20-й минуте | 38% | 42% | 58% |
| 24 плитки, iPhone 15 Pro | Устойчивый FPS | 30 | 30 | 22–27 (джиттер) |
| 24 плитки, iPhone 15 Pro | Пиковая память | ~240 МБ | ~260 МБ | ~340 МБ |
| Только чат и обрамление | Скорость разработки | База | +30% | +35–40% |
Стоит выделить два паттерна. Во-первых, гибрид находится в пределах погрешности измерения относительно чистого UIKit по устойчивому FPS — накладные расходы SwiftUI живут в основном на старте и в проходах сравнения, а не на горячем пути видео. Во-вторых, чистый SwiftUI на масштабе медленный не потому, что SwiftUI сам по себе медленный, а потому, что шаблоны по умолчанию (хранение кадров в состоянии, пересоздание representable) работают против вас. Дисциплинированное приложение на чистом SwiftUI с аккуратным @Observable-скопингом может закрыть большую часть разрыва — но к этому моменту вы уже изобрели заново шаблоны UIKit, так что честным ответом остаётся гибрид.
Для подробной настройки производительности iOS за рамками выбора фреймворка прочитайте наш сопутствующий гид о том, как оптимизировать iOS-приложения по скорости и стабильности — там рассказано про работу с Instruments, утечки памяти и оптимизацию времени запуска, и всё это умножается на выбор фреймворка.
Сравнительная матрица по возможностям
Когда цифры бенчмарков улягутся, большинство ваших решений всё равно сведётся к разрывам в возможностях — какой фреймворк даёт нужную вам функцию сегодня без обходных путей. Матрица ниже — это шпаргалка, которую мы выдаём новым сотрудникам перед их первым проектом по видео-приложению.
| Возможность | SwiftUI нативно | Нужен мост UIKit | Трудоёмкость на UIKit (дни) |
|---|---|---|---|
| Превью камеры | Нет | AVCaptureVideoPreviewLayer | 0,5–1 |
| Удалённая плитка WebRTC | Нет | RTCMTLVideoView | 1–2 |
| Picture-in-Picture | Нет | AVPictureInPictureController + делегат | 3–5 |
| Входящий звонок CallKit | Нет | CXProvider + PushKit | 3–7 |
| Блокировка ландшафтной ориентации на экран | Нет | Переопределение UIHostingController | 1 |
| Прокручиваемая сетка из 20+ плиток | LazyVGrid (нестабилен на масштабе) | Рекомендуется UICollectionView | 2–4 |
| Анимированные оверлеи на плитке | Да | — | 0 |
| Dynamic Type и VoiceOver | Да (по умолчанию) | — | 0 |
| Фильтры и LUT на Metal | Нет | MTKView + CIContext | 3–8 |
Паттерн становится очевиден, когда сканируете правый столбец: почти каждая возможность, специфичная для видео, требует моста на UIKit, и почти каждая не-видео возможность достаётся бесплатно в SwiftUI. Это и есть структурный аргумент за гибридный подход — не вкус, не запас прочности на будущее, а голая форма API платформы в 2026 году.
Нужно второе мнение по вашему экрану звонка на SwiftUI?
Мы замеряем сетки участников, ловим циклы состояния и проводим гибридные рефакторинги. Большинство аудитов находят главную причину выпадения кадров за один звонок.
Управление состоянием для живых сеток участников
Самое крупное решение по производительности и корректности в SwiftUI-приложении с видео — это форма состояния. Неправильная модель кормит петлю обратной связи: одно свойство участника меняется, SwiftUI пересравнивает всю сетку, каждый UIViewRepresentable заново вызывает updateUIView, видео замирает на долю секунды, пользователь винит ваше приложение. Сделайте модель правильно — и та же сетка удержит 30 fps на протяжении 60-минутной сессии.
В iOS 17 и новее мы настойчиво рекомендуем макрос @Observable вместо ObservableObject и @Published. @Observable отслеживает чтения на уровне свойств внутри тела каждого вью, поэтому тулбар, который читает только isMuted, не перерисовывается, когда меняется activeSpeakerID. Для целей с iOS до 17 имитируйте это несколькими мелкими классами ObservableObject, локализованными по зоне ответственности.
Три правила, которые держат сетки плавными
1. Никогда не храните данные кадров в состоянии SwiftUI. Видео-рендерер владеет своим буфером пикселей. SwiftUI владеет только размером плитки, её позицией и идентичностью участника.
2. Локализуйте состояние до наименьшей изменяющейся единицы. Отдельная ParticipantViewModel на плитку, сама являющаяся @Observable, выигрывает у единого CallModel, который публикует всех участников по любому событию.
3. Пакетируйте высокочастотные события. Обновления активного говорящего, индикаторы уровня звука и бейджи качества сети срабатывают много раз в секунду. Дросселируйте их через Combine (.throttle(for: .milliseconds(250))) или через AsyncSequence с оператором сэмплирования, прежде чем они доберутся до вью-модели.
Подключение AVFoundation, Metal и WebRTC к SwiftUI
95% iOS-приложений с видео покрывают три конвейера рендеринга. Каждый из них родной для UIKit и для каждого есть безопасный шаблон, чтобы прокинуть его в SwiftUI.
AVCaptureVideoPreviewLayer (превью локальной камеры). Оберните UIView, у которого класс слоя — AVCaptureVideoPreviewLayer, прокиньте сессию через инициализатор и установите videoGravity = .resizeAspectFill. Никогда не храните сессию в @State; держите её в синглтоне или вью-модели с явными методами старта и остановки.
AVSampleBufferDisplayLayer (свои декодированные кадры). Когда вы декодируете кадры сами — например, при фильтрации на Metal или тональном маппинге HDR — используйте AVSampleBufferDisplayLayer.enqueue(_:). Это также предпочтительный API для кастомных рендереров WebRTC. Прокидывайте через UIViewRepresentable.
RTCMTLVideoView / LiveKit VideoView (конвейер SFU). Все серьёзные WebRTC SDK в 2026 году поставляют рендерер на базе MTKView. Оборачивайте его, но кэшируйте подключения по идентификатору участника, чтобы перерисовки SwiftUI не отсоединяли и не подсоединяли трек заново. Эта одна оптимизация кэширования отвечает за большинство багов плитка мерцает
, которые мы отлаживали.
AVPlayerLayer (воспроизведение VOD внутри звонка). Повторы, режим совместного просмотра и вебинары часто требуют предзаписанного потока рядом с живым видео. Нативный для SwiftUI VideoPlayer работает, но для управления адаптивным битрейтом или кастомных оверлеев откатывайтесь на AVPlayerLayer через representable.
Безопасный сниппет превью камеры
final class PreviewView: UIView {
override class var layerClass: AnyClass { AVCaptureVideoPreviewLayer.self }
var previewLayer: AVCaptureVideoPreviewLayer {
layer as! AVCaptureVideoPreviewLayer
}
}
struct CameraPreview: UIViewRepresentable {
let session: AVCaptureSession // принадлежит вью-модели, не пересоздаётся
func makeUIView(context: Context) -> PreviewView {
let v = PreviewView()
v.previewLayer.session = session
v.previewLayer.videoGravity = .resizeAspectFill
return v
}
func updateUIView(_ view: PreviewView, context: Context) {}
}
PiP, CallKit и ориентация — хитрые куски платформы
Три платформенные функции затаскивают UIKit даже в почти-SwiftUI-приложение. О них стоит сказать явно, потому что каждый основатель, с которым мы встречаемся, предполагает, что SwiftUI закрывает их в 2026 году. Он не закрывает.
Picture-in-Picture. AVPictureInPictureController требует делегат и слой-источник — и то и другое из UIKit. Оберните свою видео-поверхность в координатор и проложите управление PiP в SwiftUI через вью-модель. Без PiP пользователи, которые переключаются на мессенджер или календарь во время звонка, теряют видео, и удержание для мобильных продуктов для встреч обваливается.
CallKit. Интерфейс входящего звонка и обработка на экране блокировки идут через CXProvider и CXCallController — это системный UI, управляемый UIKit-делегатом. SwiftUI обслуживает экран после ответа, но само событие ответа или отклонения остаётся на территории UIKit. В сочетании с PushKit для VoIP-пушей это подсистема UIKit, которая останется в вашем приложении навсегда.
Ориентация на одном экране. Зафиксировать один экран (звонок) в альбомной ориентации, пока остальная часть приложения остаётся в портретной, по-прежнему требует подкласса UIHostingController, который переопределяет supportedInterfaceOrientations. Полноценного решения на чистом SwiftUI в iOS 18 нет.
Внешняя камера и Continuity Camera. Если ваше приложение поддерживает Continuity Camera на iPadOS и macOS, либо внешние USB-камеры, то AVCaptureDevice.DiscoverySession и уведомления о смене устройства устроены в форме UIKit/AppKit. Вы обрабатываете их в сервисном слое, а не в SwiftUI.
Сетка на 20 плиток — референсная архитектура
Вот архитектура, которую мы разворачиваем, когда клиент просит экран конференций на 20+ плиток без теплового тротлинга на устройствах класса iPhone 13. Это сжатый результат примерно дюжины продакшен-внедрений в образовании, телемедицине и корпоративных встречах.
Контейнер. SwiftUI-вью владеет раскладкой и предоставляет органы управления — переключатель раскладки (сетка/говорящий), реакции, mute, выход. Она читает единственную @Observable CallViewModel, но только те свойства, которые реально отображает.
Сетка. Для менее чем 20 активных плиток — LazyVGrid со стабильным .id(participant.id) на каждой плитке. Для 20 и больше — UICollectionView внутри UIViewControllerRepresentable с предзагрузкой и diffable data sources. Переиспользование ячеек в UIKit предсказуемее ленивых стеков SwiftUI на масштабе.
Плитка. Каждая плитка — это VideoTile(participantID:), Equatable UIViewRepresentable. Нижележащий MTKView держит сервисный слой в пуле и переиспользует между участниками — никогда не выделяется покадрово.
Оверлеи. Ярлычок с именем, бейдж mute, кольцо активного говорящего, реакции — чистый SwiftUI, сидит поверх плитки в ZStack. Каждый читает отдельные маленькие модели, локализованные на участника.
Подписки. Модель звонка подписывается только на верхние N активных треков (обычно N=9 в режиме говорящего, N=20 в сетке) и сообщает SFU, какие слои simulcast присылать. Это трюк, который позволяет классу на 200 участников держать 30 fps: пользователь видит максимум 20 плиток, SFU присылает только эти потоки, а устройство никогда не декодирует больше, чем показывает.
Управление температурой и энергией. Пробросьте ProcessInfo.thermalState в модель звонка. Когда тепловое состояние достигает .serious или .critical, понижайте плитки не-говорящих до режима только-аудио и сокращайте слой simulcast до 180p. Пользователи скорее согласятся видеть меньше лиц, чем смотреть, как телефон выключается.
Мини-кейс — чему нас научил выпуск ChillChat и Speakk
ChillChat — это социальная видео-сеть, где незнакомцы встречаются в тематических комнатах до 8 участников. Мы построили iOS-приложение на SwiftUI для всех не-встречных поверхностей — онбординг, каталог комнат, профили, инструменты модерации — и спрятали живые плитки за рендерером UIKit. С аккуратным @Observable-скопингом и попартисипантным .id() мы держим 30 fps на iPhone 11 и новее, при этом срезав примерно 4 недели с графика благодаря предпросмотру и декларативным спискам SwiftUI.
Speakk — мессенджер в стиле WhatsApp с видеозвонками один на один и в небольших группах. Здесь поверхность звонка проще — одна или две плитки — так что мы продвинули SwiftUI дальше, оставив на UIKit только превью камеры и удалённый рендерер. Компромисс: больше скорости фич в чате, но нам всё равно понадобились CallKit, PushKit и PiP, а это всё UIKit. Урок: в основном SwiftUI
всё равно означает, что вы держите три UIKit-подсистемы.
В обоих продуктах паттерном, который значил больше всего, оказался единый сервис CallController, который владел AVFoundation, WebRTC-треками и жизненным циклом рендерера. Слой интерфейса — SwiftUI или UIKit — только спрашивал дай вью для участника X
и никогда не трогал медиа напрямую. Эта одна архитектурная граница сделала оба приложения тестируемыми, позволила нам поменять видео-SDK (с Twilio на LiveKit на одном из них) без переписывания интерфейса и удержала баг-репорты подальше от графа состояний SwiftUI.
Если вам нужен похожий архитектурный обзор по вашему приложению, 30-минутного консультационного созвона обычно достаточно, чтобы вытащить два главных риска в вашем текущем стеке и наименьший рефакторинг, который стабилизирует экран звонка.
Фреймворк решений — выберите стек за пять вопросов
Пройдитесь по этим пяти вопросам по порядку. Каждый из них либо подтвердит вашу текущую интуицию, либо подтолкнёт к другому разделению.
В1. Сколько одновременных видеоплиток должно быть на экране? 1–2 плитки: SwiftUI-ориентированный подход подойдёт. 3–12 плиток: гибрид. 12+ плиток или прокручиваемые сетки: UIKit-ориентированный подход или UICollectionView внутри representable.
В2. Какое у вас минимально поддерживаемое устройство? iPhone 13 и новее: подойдёт любой выбор. iPhone 11/12 или iPad mini 5: гибрид или UIKit. iPhone 8/X на iOS 16: безопасный выбор по умолчанию — UIKit.
В3. Нужны ли вам PiP, CallKit или ориентация на отдельных экранах? Любой из этих пунктов означает, что UIKit входит в вашу кодовую базу. Закладывайте слой хостинга UIKit с первого дня, а не дотягивайте его задним числом.
В4. Сколько времени до первого запуска? 8 недель или меньше: гибрид с SwiftUI-ориентированным обрамлением выигрывает по скорости. 16+ недель: выбирайте разделение, которое минимизирует долгосрочную поддержку — часто это UIKit-ориентированный подход на экране встречи.
В5. Насколько опытна ваша iOS-команда в Metal, AVFoundation и WebRTC? Если ответ — невысоко
, оставайтесь с SDK (LiveKit, Daily, 100ms), который даёт готовый рендерер, и используйте SwiftUI только для обрамления. Если же вы катите собственный видео-конвейер, сторона UIKit растёт быстро.
Подводные камни, которых стоит избегать
1. Хранение кадров в @State. Одного @State var currentFrame: CVPixelBuffer? достаточно, чтобы обвалить производительность на сетке из 4 плиток. Держите кадры вне SwiftUI — всегда.
2. Пересоздание UIViewRepresentable при каждой перерисовке. Если ваш representable определён inline внутри вью, которое перерисовывается, вы получаете новый вызов makeUIView каждый раз. Вынесите структуру наружу, сделайте её Equatable и прикрепите .id(participant.id).
3. LazyVGrid внутри ScrollView без стабильных ID. Прокручиваете сетку — плитки уничтожаются и пересоздаются, видео стартует заново. Всегда прикрепляйте стабильные идентичности и предзагружайте треки следующего ряда до того, как пользователь долистает.
4. Выпуск без трейсов Instruments. Если у вас нет трейса time-profiler в режиме до и после
по 10-минутному звонку, в следующем релизе вы выпустите регрессию производительности. Сессия с WWDC25 Optimize SwiftUI Performance with Instruments
стоит ваших 60 минут.
5. Игнорирование теплового и энергетического состояния. Звонок, который перегревает телефон за 10 минут, проваливает корпоративные пилоты и убивает рейтинг в App Store. Слушайте ProcessInfo.thermalState и isLowPowerModeEnabled; деградируйте плавно.
KPI, которые стоит измерять после запуска
1. KPI качества. Медианный FPS принимаемого видео на плитку (цель: ≥ 24 на iPhone 11 и новее), оценка MOS для аудио (цель: ≥ 4,0), число замираний на 30-минутный звонок (цель: < 2) и время до первого кадра после подключения (цель: < 1,5 секунды).
2. Бизнес-KPI. Доля успешных звонков (цель: > 95%), доля отказов на прелобби (цель: < 10%), удержание на неделю для пользователей, у которых был хотя бы один звонок (цель: > 40%), и средняя длительность звонка. Это те цифры, которые волнуют вашу продуктовую команду; инженерные решения в итоге проявляются в них.
3. KPI надёжности. Доля пользователей без падений (цель: > 99,5%), доля сессий с зависанием главного потока (цель: < 0,5%) и потребление памяти p95 на 30-минутном звонке с 8 плитками (цель: < 300 МБ на iPhone 12). Выкатите эти дашборды на той же неделе, на которой выкатываете первый экран встречи.
Когда НЕ выбирать SwiftUI
Три случая, когда мы подталкиваем клиентов к UIKit-only даже в 2026 году. Первый: если вам обязательно поддерживать iOS 12 или старше — всё ещё распространённый сценарий в корпоративных развёртываниях с MDM-залоченными устройствами — SwiftUI недоступен. Второй: если ваше приложение — нишевый инструмент для вещания с тяжёлыми Metal-конвейерами (AR-фильтры, виртуальные фоны, композитинг оверлеев, LUT), весь стек рендеринга настолько UIKit-ориентирован, что SwiftUI почти ничего не даёт и стоит вам ещё одного шва для отладки.
Третий: если у вашей команды десять лет опыта на UIKit и ноль на SwiftUI, а до критичного запуска у вас 12 недель, кривая обучения не стоит риска. SwiftUI прощает в приложениях со списками и беспощаден в видео-приложениях на дедлайне. Мы видели, как опытные UIKit-команды теряли по три недели на один баг с циклом состояния. Начинайте гибрид после запуска, а не во время.
Влияние на стоимость и сроки
Выбор фреймворка меняет ваши сроки сильнее, чем большинство основателей ожидает. На обрамлении (70% экранов, которые не являются экраном встречи) SwiftUI экономит около 30–40% времени разработки по сравнению с UIKit. На экране встречи он обычно отъедает часть этой экономии обратно, пока вы боретесь с состоянием и проблемами representable. В сухом остатке для гринфилд-приложения для звонков на iOS это выигрыш по скорости в 15–20%, если гибрид дисциплинированный, и нулевой результат или регрессия, если команда наивно пытается использовать чистый SwiftUI на встрече.
Затраты на поддержку расходятся позже. UIKit-only приложения проще отлаживать, когда что-то ломается в звонке, потому что каждый ответ на форуме сформулирован под UIKit. SwiftUI-приложения дешевле развивать под продуктовые фичи вроде новых раскладок чата, реакций и доработок онбординга. Гибридный подход не самый дешёвый ни по одному измерению, но это наименее рискованный выбор на горизонте продукта в 24 месяца.
Поскольку мы объединяем Agent Engineering с нашей iOS-командой, оценки Фора Софт обычно выходят быстрее и дешевле, чем у традиционных подрядчиков, на обрамлении, и примерно на одном уровне на движке встречи — это та часть, где физика всё ещё доминирует. Если вам нужен реалистичный диапазон по вашему скоупу, поделитесь с нами на звонке целевым числом участников, минимальным устройством и сроком запуска, и мы пришлём пронумерованную оценку в течение 48 часов.
Нужна пронумерованная оценка для вашего видео-приложения?
Расскажите про целевые устройства, число участников и срок запуска. Мы пришлём проработанный план в течение 48 часов после звонка.
FAQ
Может ли SwiftUI в одиночку справиться с корпоративными видеоконференциями на 20+ участников?
Технически да, прагматически нет. На 20+ плитках механизм сравнения SwiftUI и переиспользование LazyVGrid вступают в конфликт с видео-конвейером, и выпущенные корпоративные приложения прячут сетку за UICollectionView ради предсказуемости. UIKit-полотно внутри SwiftUI-оболочки — стандартный шаблон.
Совместим ли SwiftUI со старыми версиями iOS для видеоконференций?
SwiftUI выходит с iOS 13+, но фичи, важные для видео-приложений (@Observable, современная навигация, улучшенная производительность раскладки), требуют iOS 17 или новее. Для приложения, которое должно работать на iOS 12 или старше — всё ещё распространённый сценарий в корпоративных парках устройств — UIKit остаётся единственным вариантом.
Как SwiftUI и UIKit соотносятся по расходу батареи во время звонков?
Наши замеры показывают, что SwiftUI потребляет на 8–15% больше батареи, чем UIKit, на 30-минутных звонках с 4+ плитками, и связано это с дополнительными проходами сравнения и перерисовками вью. На звонке один на один разница незаметна. На звонке на 12 человек это может вылиться в 10–15 минут меньше времени разговора на одном заряде, что важно для корпоративных командировочников
.
Нужно ли писать собственный рендерер, или можно использовать SDK?
Если только ваш продукт не специализированный инструмент для вещания, используйте SDK. LiveKit, Daily, 100ms, Twilio, Agora и Vonage поставляют оптимизированные UIKit-рендереры. Вы оборачиваете их в UIViewRepresentable и фокусируете инженерные усилия на продукте, а не на видео-сантехнике. Мы разбираем компромиссы между SDK подробно в нашем гиде по альтернативам Agora.io.
Какая кривая обучения у UIKit-разработчиков при переходе на SwiftUI?
От двух до четырёх недель для базовой беглости в списках и формах. От восьми до двенадцати недель, прежде чем разработчик интуитивно понимает, когда использовать @State, когда @Observable, а когда Binding на чувствительных к производительности поверхностях. Слой моста (UIViewRepresentable, координаторы) добавляет ещё от четырёх до шести недель. Закладывайте полный квартал, прежде чем команда выпустит продакшен-грейд видео на SwiftUI.
Есть ли готовые UI-компоненты для видеоконференций в SwiftUI?
Да — Stream, LiveKit и 100ms поставляют дружественные SwiftUI библиотеки компонентов для плиток, элементов управления и списков участников. Это хорошая стартовая точка для MVP. Для брендированного продукта вы всё равно будете их сильно кастомизировать, но дефолтный интерфейс экономит примерно две-три недели бойлерплейта.
Как обрабатывать Picture-in-Picture в SwiftUI?
Оберните AVPictureInPictureController в координатор, прикреплённый к тому же UIViewRepresentable, что и ваш видео-рендерер. Управляйте состоянием PiP из SwiftUI через @Observable-модель. Нативного API PiP для SwiftUI в iOS 18 нет, и попытки впихнуть его без UIKit — источник большинства баг-репортов PiP не запускается
.
Стоит ли мигрировать существующее UIKit-приложение с видео на SwiftUI?
Редко — как полная переписка; почти всегда — как постепенная миграция обрамления и настроек с сохранением экрана встречи нетронутым. Полная переписка зрелого UIKit-приложения для звонков занимает 6–12 месяцев и обычно выходит с регрессией производительности. Гибридная миграция сохраняет стабильные части и даёт ускорение там, где оно нужно.
Что почитать дальше
Архитектура iOS
Playbook по MVVM-C для iOS на 2026 год
SwiftUI @Observable, координаторы, DI и Swift 6 concurrency — архитектурный слой за каждым гибридным видео-приложением.
Swift 6
Разработка на iOS со Swift 6 — видеочат нового поколения
Строгая конкурентность, защита от data race и как новая модель Swift вписывается в видеочаты на SwiftUI или UIKit.
Стек WebRTC
Альтернатива Agora.io в 2026 году
Кастомный WebRTC с LiveKit, mediasoup, Jitsi и Janus — что подобрать к фронтенду на SwiftUI или UIKit.
Производительность
Как оптимизировать iOS-приложения по скорости и стабильности
Работа с Instruments, время запуска, утечки памяти — KPI, которые умножаются на выбор фреймворка в видео-приложениях.
Управление зависимостями
Swift Package Manager для видео-приложений
Как держать LiveKit, WebRTC, Starscream и аналитические SDK воспроизводимыми и с бинарным кэшем — гид разработчика на 2025 год.
Готовы выпустить видео-приложение, которое реально масштабируется?
Вопрос SwiftUI или UIKit не идеологический, а архитектурный. Выпускайте обрамление на SwiftUI, выпускайте полотно встречи на UIKit за тонким representable и склейте их дисциплинированной @Observable-моделью. Это разделение даёт вам 30–40% скорости на большей части приложения, сохраняя при этом ту часть, по которой вас оценивают пользователи — живое видео — плавной, предсказуемой и переносимой между SDK.
В Фора Софт мы использовали этот шаблон на 625+ выпущенных продуктах, от социального видео вроде ChillChat до корпоративного обучения вроде BrainCert. Если вам нужен проработанный план для вашего приложения — число участников, минимальное устройство, срок запуска, честные компромиссы — приходите со своим самым сложным крайним случаем на 30-минутный звонок. Agent Engineering превращает этот звонок в пронумерованную оценку за 48 часов.
Принесите самый сложный вопрос по видео-приложению на 30-минутный звонок
Расскажите про целевые устройства, число участников и срок запуска. Мы пришлём набросок гибридной архитектуры и проработанную оценку в течение 48 часов.

