Однонаправленный поток данных

Все механизмы состояния складываются в единый принцип: данные текут в одну сторону — от источника истины к интерфейсу, а события идут обратно, меняя источник.
Суть урока: один источник истины, поток вниз к вью, события вверх к источнику. Этот цикл делает поведение приложения предсказуемым и отлаживаемым.

Соберём картину целиком. В SwiftUI данные движутся однонаправленно. Есть источник истины (@State или @Observable-модель). Из него данные текут вниз по дереву вью, формируя интерфейс. Когда пользователь что-то делает, вью отправляет событие наверх, которое меняет источник истины. И цикл повторяется — новое состояние рождает новый интерфейс.

        +-----------------------------+
        |     ИСТОЧНИК ИСТИНЫ          |
        |  @State / @Observable       |
        +-----------------------------+
           |                    ^
  данные   | вниз          вверх | событие
  (состояние)                   (action)
           v                    |
        +-----------------------------+
        |          ВЬЮ (UI)           |
        |  показывает состояние,       |
        |  шлёт события по действиям    |
        +-----------------------------+

Почему это важно? Потому что в любой момент состояние приложения полностью описывается источником истины. Чтобы понять, что на экране, достаточно посмотреть на данные. Нет скрытых изменений «где-то в обработчике». Это резко упрощает отладку: воспроизвели состояние — воспроизвели экран.

@Observable
class Counter { var value = 0 }

struct CounterScreen: View {
    @State private var counter = Counter()
    var body: some View {
        VStack {
            Text("\(counter.value)")     // данные ВНИЗ
            Button("+") { counter.value += 1 }   // событие ВВЕРХ
        }
    }
}

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

# Цикл состояние -> вью -> событие -> новое состояние как редьюсер.
def reducer(state, action):
    if action == 'increment':
        return {'value': state['value'] + 1}
    if action == 'reset':
        return {'value': 0}
    return state

def view(state):                 # данные текут вниз
    return f"[ Счёт: {state['value']} ]"

state = {'value': 0}
print(view(state))
for action in ['increment', 'increment', 'reset', 'increment']:
    state = reducer(state, action)   # событие меняет источник истины
    print(action, '->', view(state))

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

Однонаправленный поток — это не отдельный API, а следствие того, как устроены @State, @Binding и @Observable. Чтение свойства регистрирует зависимость, запись планирует перерисовку. Поскольку вью не хранят собственную «теневую» копию данных, а каждый раз выводят интерфейс из источника истины, рассинхронизация невозможна. Этот паттерн перекликается с архитектурами вроде Redux/TCA, но в SwiftUI он встроен в саму модель данных.

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

  • Дублировать состояние. Локальная копия источника истины приводит к рассинхрону.
  • Менять данные в обход потока. Прямые мутации вне реактивной системы интерфейс не обновят.
  • Хранить производные данные. То, что вычисляется из состояния, лучше считать на лету (вычисляемое свойство).

Best practices

  • Держите единственный источник истины для каждой единицы данных.
  • Производные значения вычисляйте, а не дублируйте.
  • Думайте циклом: состояние → интерфейс → событие → новое состояние.

Итоги. Однонаправленный поток данных связывает @State, @Binding и @Observable в стройную систему: один источник истины, данные вниз, события вверх. Этот принцип делает SwiftUI-приложения предсказуемыми и легко отлаживаемыми.

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

Однонаправленный поток данных — это не изобретение SwiftUI, а проверенный индустрией паттерн, знакомый по архитектурам Redux в вебе и The Composable Architecture в мире Swift. Его сила в предсказуемости: раз состояние приложения целиком описывается источником истины, любой баг воспроизводится воспроизведением состояния, а не охотой за скрытыми мутациями в разрозненных обработчиках. Это резко упрощает и отладку, и тестирование, и совместную работу в команде. Производные данные в этой модели не хранят, а вычисляют на лету через вычисляемые свойства — так исключается рассинхронизация между «исходником» и «копией». По мере роста приложения вы, возможно, придёте к более формальным архитектурам со слоем редьюсеров и явными действиями, но фундамент везде один: единый источник истины, данные вниз, события вверх. Усвоив этот цикл сейчас, на маленьких примерах, вы заложите мышление, которое масштабируется на приложения любой сложности.

Проверьте себя
1. В каком направлении движутся данные и события в SwiftUI?
AДанные вверх, события вниз
BДанные вниз к вью, события вверх к источнику истины
CИ то и другое в обе стороны хаотично
DТолько внутри одного вью
2. Почему дублирование источника истины — антипаттерн?
AЗамедляет компиляцию
BПриводит к рассинхронизации интерфейса и данных
CЗапрещено компилятором
DУвеличивает размер приложения