Добавить объявление

ContentBuilder в SwiftUI: секрет ускорения проверки типов

На WWDC 2026 компания Apple представила ContentBuilder. Выглядит он как ViewBuilder с более широким охватом. Но главное - Apple заявляет о значительном ускорении проверки типов.

Как работает ViewBuilder:


ViewBuilder - это result builder, механизм Swift, который превращает декларативный код в вызовы методов. Когда вы пишете несколько View подряд, ViewBuilder собирает их в единую структуру:

@ViewBuilder
var content: some View {
Text("Hello")
Image(systemName: "star")
}

Это не магия SwiftUI, а обычный result builder из Swift.

В чем была проблема:


Со временем в SwiftUI добавили много сущностей: Scene, Toolbar, Commands, Table. У каждого свой result builder. А общие компоненты вроде Group, ForEach и Section получили инициализаторы для каждого из них.

Вот как выглядел Group раньше:

extension Group: View where Content: View {
init(@ViewBuilder content: () -> Content)
}

extension Group: ToolbarContent where Content: ToolbarContent {
init(@ToolbarContentBuilder content: () -> Content)
}

extension Group: Commands where Content: Commands {
init(@CommandsBuilder content: () -> Content)
}

Когда компилятор встречал Group, он не знал, какой инициализатор выбрать. Приходилось проверять все варианты. И так на каждом уровне вложенности.

Что изменилось:


Apple переработала инициализаторы и возвращаемые типы builder.

Новые методы не содержат ограничений вроде Content: View или Content: Commands.

public static func buildBlock<Content>(
_ content: Content
) -> Content

public static func buildBlock<each Content>(
_ content: repeat each Content
) -> TupleContent<repeat each Content>

Появился TupleContent. Он не принадлежит никакой сущности. Это View только тогда, когда все его элементы - View. Когда все элементы - ToolbarContent, он становится содержимым панели инструментов.

То же самое для Optional, _ConditionalContent, Group и ForEach.

Как это работает:


Раньше компилятор на каждом уровне вложенности выбирал между несколькими инициализаторами. Теперь он сначала строит структуру, а потом проверяет ее соответствие нужному протоколу.

Для Group новый интерфейс выглядит так:

public struct Group<Content> { ... }

extension Group {
public init(@ContentBuilder content: () -> Content)
}

extension Group: View where Content: View {}
extension Group: ToolbarContent where Content: ToolbarContent {}

Компилятор больше не перебирает варианты на каждом шаге. Он определяет Content, а потом Group получает нужную возможность через условное соответствие.

Что это дает:


Проверка типов стала быстрее. Особенно в местах с глубокой вложенностью Group, ForEach и Section.

Но это не значит, что весь код компилируется в разы быстрее. Ускорение касается конкретных компонентов с перегрузками. И это не влияет на производительность приложения во время работы.

Есть ли ограничения:


Да. Поскольку ограничения ослаблены, в некоторых случаях компилятор может запутаться. Например, .overlay(Color.blue.opacity(0.2)) может стать неоднозначным. Использование замыкания решает проблему.

.overlay {
Color.blue.opacity(0.2)
}

Если импортирован MapKit, пустой блок Group {} может не найти нужный тип. Явное указание EmptyView() помогает.

За пределами SwiftUI:


ContentBuilder можно применять для собственных DSL. Если ваши типы реализуют нужные протоколы, структура будет собираться так же.

Вывод:


ContentBuilder - это не новый result builder. Это typealias на ViewBuilder. Но за ним стоит переработка API, которая убирает лишние перегрузки и ускоряет проверку типов.

Идея простая: сначала построить структуру, потом проверить ее возможности. Это уменьшает количество вариантов, которые компилятор должен перебрать на каждом уровне вложенности.

Apple не акцентировала внимание на ContentBuilder. Для большинства разработчиков он незаметен. Но для тех, кто сталкивался с медленной компиляцией SwiftUI, это важное изменение.
19.10.2026 7 667