@State и локальное состояние вью

@State — это способ дать вью собственную изменяемую память. Когда состояние меняется, SwiftUI автоматически перерисовывает вью.
Суть урока: вью — структура, а значит неизменяема. @State — это обёртка (property wrapper), которая хранит данные ВНЕ структуры и связывает их со SwiftUI, делая вью реактивным.

Возникает парадокс: вью — это структура, её свойства неизменяемы, но интерфейс должен меняться. Решение — property wrapper @State. Он хранит значение в специальном месте, которым управляет SwiftUI, а не внутри самой структуры:

struct ToggleView: View {
    @State private var isOn = false

    var body: some View {
        VStack {
            Text(isOn ? "Включено" : "Выключено")
            Button("Переключить") {
                isOn.toggle()      // меняем — вью перерисуется
            }
        }
    }
}

Хотя body формально не может менять свойства структуры, @State позволяет это: при изменении isOn SwiftUI замечает, что вью зависело от этого значения, и заново вызывает body. @State-свойство — это источник истины (source of truth) для данных, которыми владеет именно это вью.

Важное правило: @State почти всегда помечают private. Оно принадлежит только этому вью; другие вью не должны лезть в него напрямую. И используется оно для простых, локальных данных — флажков, введённого текста, выбранной вкладки.

Структура вью (неизменяема)
   +-------------------------------+
   |  @State private var isOn      |---+
   +-------------------------------+   |
                                        v
            SwiftUI хранит значение ОТДЕЛЬНО
                 |  isOn меняется
                 v
           body вызывается заново  ->  экран обновлён

Попробуй сам ▶ — запусти код прямо в браузере (Pyodide). Здесь нет Swift, но логика та же, что под капотом мобильного кода:

# Источник истины как единственное место хранения состояния.
class ViewState:
    def __init__(self):
        self._is_on = False     # хранится отдельно, как @State
    def toggle(self):
        self._is_on = not self._is_on
        self.render()           # SwiftUI вызвал бы body сам
    def render(self):
        print('Включено' if self._is_on else 'Выключено')

v = ViewState()
v.render()
v.toggle()
v.toggle()

Как работает под капотом

Property wrapper — это обёртка над свойством с дополнительной логикой. @State прячет значение в управляемом SwiftUI хранилище, привязанном к идентичности вью, а не к экземпляру структуры (которая пересоздаётся при каждой перерисовке). Когда вы меняете значение, SwiftUI помечает вью как «грязное» и планирует пересчёт его body. Так данные переживают пересоздание структуры, а интерфейс остаётся синхронным с состоянием.

Частые ошибки

  • Использовать @State для данных, которыми владеет другое вью. Тогда нужен @Binding или @Observable.
  • Делать @State не приватным. Это нарушает инкапсуляцию владения.
  • Хранить в @State тяжёлые модели. Он задуман для простого локального состояния.

Best practices

  • Помечайте @State как private — состояние принадлежит только этому вью.
  • Используйте @State для лёгких локальных данных: флажков, текста, выбора.
  • Для общих и сложных данных переходите к @Observable из следующих уроков.

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

Шире контекста

Property wrappers вроде @State — одна из самых изящных идей Swift: они позволяют прикрепить к свойству дополнительное поведение, спрятав сложную механику за простым синтаксисом. Снаружи @State private var isOn = false выглядит как обычная переменная, но внутри это целая система хранения и оповещения, связанная с движком отрисовки SwiftUI. Ключевая тонкость для понимания: структура вью пересоздаётся при каждой перерисовке, а вот хранилище @State живёт дольше, привязанное к идентичности вью в дереве, а не к конкретному экземпляру структуры. Именно поэтому ваш счётчик не сбрасывается при каждом обновлении экрана. Правильное разделение ответственности — @State для простого локального состояния, @Observable для разделяемых моделей — это то, что отличает чистую SwiftUI-архитектуру от запутанной. На старте достаточно запомнить: маленькие локальные данные, которыми владеет именно это вью, — это работа для @State.

Проверьте себя
1. Почему @State необходим, если вью — это неизменяемая структура?
AОн делает структуру классом
BОн хранит значение вне структуры, в управляемом SwiftUI месте
CОн отключает проверку типов
DОн ускоряет компиляцию
2. Как обычно следует помечать @State-свойство?
Apublic
Bstatic
Cprivate
Dlazy