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

@State в SwiftUI стал macro. Синтаксис не изменился, поведение стало лучше

На WWDC26 компания Apple перевела @State с property wrapper на macro. Для разработчиков это значит, что код останется таким же, как и раньше, но внутри все работает иначе. Основное изменение касается ленивой инициализации observable-объектов, хранящихся в @State.

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


Раньше @State был property wrapper. Теперь это attached macro. В документации указано:

@attached(accessor, names: named(init), named(get), named(set))
@attached(peer, names: prefixed(`_`), prefixed(__), prefixed(`$`))
macro State()

На практике синтаксис не меняется. Код, который вы писали раньше, продолжает работать. Разница только в том, как SwiftUI управляет хранением состояния.

Изменения на практике:


Самое важное - ленивая инициализация для observable-классов в @State.

Раньше, если вы писали так:

@Observable
class ViewModel {
var index = 0

init() {
print("Init")
}
}

struct MyView: View {
@State private var viewModel = ViewModel()

var body: some View {
Button("Index: \(viewModel.index)") {
viewModel.index += 1
}
}
}

Инициализатор ViewModel мог вызываться каждый раз, когда SwiftUI пересоздавал структуру вью. Даже если сам объект хранился и не терялся, его инициализация все равно выполнялась. Это приводило к лишней работе, особенно если в инициализаторе были тяжелые операции.

Теперь, с macro версией @State, объект создается один раз - когда SwiftUI создает хранилище состояния для вью. И больше не пересоздается при каждом обновлении родителя.

Когда это особенно заметно:


Это изменение особенно полезно, когда вы храните в @State тяжелые объекты: модели с подписками, кэшами, сетевыми запросами при инициализации. Раньше приходилось использовать обходные пути - например, хранить опциональный объект и создавать его в .task.

// Раньше приходилось делать так, чтобы избежать повторной инициализации
@State private var viewModel: ViewModel?

var body: some View {
MyView(viewModel: viewModel)
.task {
viewModel = ViewModel()
}
}

Теперь это больше не нужно. Можно просто писать:

@State private var viewModel = ViewModel()

И быть уверенным, что объект создастся только один раз.

Важно:


Есть одна тонкость, которая осталась. Если вы инициализируете @State внутри init() вью, а не прямо в объявлении, конструктор может вызываться несколько раз. Это не значит, что состояние сбросится, но сам init выполнится, и все побочные эффекты произойдут.

init(id: ID) {
_viewModel = State(initialValue: ViewModel(id: id))
}

Если объект зависит от данных, которые приходят извне и могут меняться, лучше использовать другой подход - task(id:) или передавать зависимости явно через параметры вью.

Вывод:


Apple сделала @State macro, чтобы улучшить производительность в типичных сценариях. Синтаксис остался прежним, код не требует изменений, но observable-объекты в @State теперь инициализируются один раз. Это избавляет от лишнего кода и делает поведение более предсказуемым.

Переход на macro для такого базового механизма - это важный сигнал. Apple уверена в своей macro-системе и готова переносить на нее ключевые API. А для разработчиков это просто означает, что SwiftUI становится чуть более эффективным и предсказуемым.
20.06.2026 25 578