Ответ
Это два противоположных подхода к управлению состоянием и его изменением в приложении.
Однонаправленный поток данных (Unidirectional Data Flow, UDF)
Данные и события циркулируют в приложении по строго заданному циклу, обычно: Состояние → Представление → Действие → Состояние. Это делает изменения состояния предсказуемыми и упрощает отладку.
Классическая модель (Redux/Flux):
// 1. Единый источник истины — State
struct AppState {
var username: String = ""
var isLoading: Bool = false
}
// 2. Действия (Actions) — описывают что произошло
enum Action {
case setUsername(String)
case setLoading(Bool)
}
// 3. Редуктор (Reducer) — чистая функция, создающая новое состояние
func reducer(state: AppState, action: Action) -> AppState {
var newState = state
switch action {
case .setUsername(let name):
newState.username = name // Иммутабельное обновление
case .setLoading(let loading):
newState.isLoading = loading
}
return newState
}
// 4. Представление (View) только отображает состояние и отправляет действия
struct ProfileView: View {
let state: AppState
let dispatch: (Action) -> Void // Функция отправки действия
var body: some View {
VStack {
TextField("Name", text: Binding(
get: { state.username },
set: { newName in dispatch(.setUsername(newName)) } // Одно направление
))
if state.isLoading { ProgressView() }
}
}
}
Преимущества UDF: предсказуемость, простота отладки (логи можно записывать), тестируемость (редуктор — чистая функция).
Двунаправленный поток данных (Bidirectional / Two-Way Binding)
Компоненты UI напрямую связаны с данными модели. Изменение в UI мгновенно обновляет модель, и наоборот.
Пример в SwiftUI с @Binding:
struct ContentView: View {
@State private var text: String = "" // Источник данных
var body: some View {
// Двусторонняя привязка: изменение TextField меняет `text`, и наоборот
TextField("Enter text", text: $text)
Text("You typed: (text)")
}
}
Пример во фреймворке с реактивным программированием (Combine):
class LoginViewModel: ObservableObject {
@Published var username = "" // Изменения автоматически публикуются
@Published var password = ""
// Изменение в текстовом поле → обновляет @Published свойство → View перерисовывается
}
Сравнение подходов
| Параметр | Однонаправленный поток | Двунаправленная привязка |
|---|---|---|
| Направление данных | Строго одно: State → View → Action → State | Взаимное: Model ⇄ View |
| Сложность отладки | Низкая (логи линейны) | Высокая (циклические зависимости) |
| Контроль | Полный контроль над каждым изменением | Автоматизация, меньше boilerplate-кода |
| Тестируемость | Отличная | Сложнее из-за неявных связей |
| Типичные use-case | Сложные бизнес-процессы, командная работа | Быстрые прототипы, простые формы, UI-логика |
Современный подход: гибридный. Используйте однонаправленный поток для глобального состояния приложения (например, через Reducer), а двунаправленные привязки — для локального состояния компонента (например, текст в поле ввода).