0Pricing
Swift Academy · Урок

Состояние, действие и редьюсер

Моделируйте возможности приложения как чистые редьюсеры, работающие с состоянием.

«Состояние, действие и редьюсер» — бесплатный урок Swift Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.

Что такое Composable Architecture

Composable Architecture, или TCA, — это библиотека для создания приложений на Swift с единообразной структурой, удобной для тестирования. Она основана на трёх элементах: State, описывающем данные компонента; Action, описывающем каждое возможное событие; и Reducer, изменяющем состояние в ответ на действия.

Этот однонаправленный поток делает компоненты предсказуемыми и удобными для тестирования.

import ComposableArchitecture

// State holds data, Action describes events,
// a Reducer turns (state, action) into the next state.

Макрос @Reducer

Компонент определяется как тип, отмеченный макросом @Reducer. Макрос генерирует вспомогательный код и позволяет объявить State, Action и тело редуктора body вместе, в одном цельном блоке.

По соглашению тип называют в соответствии с компонентом, например CounterFeature.

import ComposableArchitecture

@Reducer
struct CounterFeature {
    // State, Action, and body go here
}

Определение State

State обычно представляет собой структуру, содержащую все данные, необходимые компоненту. Пометка @ObservableState позволяет SwiftUI точно наблюдать за изменениями и повторно отображать только затронутые части.

Сохраняйте состояние минимальным, а производные значения вычисляйте, чтобы существовал единый источник истины.

import ComposableArchitecture

@Reducer
struct CounterFeature {
    @ObservableState
    struct State: Equatable {
        var count = 0
    }
}

Определение действий

Action — это перечисление, содержащее все события, которые может обработать компонент: нажатия пользователя, ответы, срабатывания таймера и так далее. Каждый вариант описывает произошедшее событие, а не команду для непосредственного изменения состояния.

Понятные имена действий делают поведение компонента самодокументируемым.

import ComposableArchitecture

@Reducer
struct CounterFeature {
    @ObservableState
    struct State: Equatable { var count = 0 }

    enum Action {
        case incrementTapped
        case decrementTapped
    }
}

Тело Reducer

Логика редуктора находится в вычисляемом свойстве body, которое возвращает некоторый ReducerOf<Self>. Внутри используется Reduce с замыканием, получающим изменяемые state и входящее action.

Вы перебираете варианты действия и соответственно изменяете состояние.

import ComposableArchitecture

@Reducer
struct CounterFeature {
    @ObservableState
    struct State: Equatable { var count = 0 }
    enum Action { case incrementTapped, decrementTapped }

    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .incrementTapped:
                state.count += 1
                return .none
            case .decrementTapped:
                state.count -= 1
                return .none
            }
        }
    }
}

Возврат эффектов

Каждая ветвь редуктора должна возвращать Effect. Возврат .none означает, что после изменения состояния больше не требуется выполнять асинхронную работу.

Если нужны побочные эффекты, например сетевой запрос, верните эффект, выполняющий эту работу. Эффекты будут подробно рассмотрены в следующем уроке.

import ComposableArchitecture

// Every reducer branch returns an Effect.
// No side effect needed? Return .none.
// Need async work? Return an effect describing it.
Reduce { state, action in
    state.count += 1
    return .none
}

Изменения состояния централизованы

Одно из основных правил TCA гласит: состояние изменяет только редуктор и только в ответ на действия. Представления никогда не изменяют состояние напрямую — они отправляют действия.

Именно это единое место для изменений делает поток данных простым для понимания и тестирования.

import ComposableArchitecture

// Views send actions; the reducer mutates state.
// store.send(.incrementTapped)  ->  state.count += 1
// No direct state mutation outside the reducer.

Моделирование пользовательского ввода в State

Компоненты, похожие на формы, хранят редактируемые поля в State и добавляют действия для их изменения. Привязка свойств позволяет элементам управления SwiftUI обновлять их через действия с проверкой типов.

В этом примере компонент отслеживает текстовое поле и переключатель.

import ComposableArchitecture

@Reducer
struct FormFeature {
    @ObservableState
    struct State: Equatable {
        var name = ""
        var isSubscribed = false
    }
    enum Action: BindableAction {
        case binding(BindingAction<State>)
    }
    var body: some ReducerOf<Self> {
        BindingReducer()
    }
}

Действия с ассоциированными значениями

Действия могут переносить данные с помощью ассоциированных значений. Так моделируют возвращаемые результаты, например загруженный элемент или ошибку, чтобы редуктор мог сохранить их в состоянии.

Здесь действие ответа содержит полученное число.

import ComposableArchitecture

@Reducer
struct NumberFeature {
    @ObservableState
    struct State: Equatable { var value = 0 }
    enum Action {
        case reloadTapped
        case responseReceived(Int)
    }
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .reloadTapped:
                return .none
            case let .responseReceived(value):
                state.value = value
                return .none
            }
        }
    }
}

Почему сравнимость состояния полезна

Соответствие State протоколу Equatable позволяет TCA определить, действительно ли состояние изменилось, что обеспечивает точное обновление представлений и исчерпывающее тестирование. Это небольшое требование даёт большие преимущества.

Большинство типов состояния могут автоматически соответствовать Equatable, поскольку их сохранённые свойства уже ему соответствуют.

import ComposableArchitecture

@ObservableState
struct State: Equatable {
    var count = 0
    var title = ""
}
// Equatable enables change detection and test assertions.

Структура полного компонента

Готовый компонент читается сверху вниз: макрос, наблюдаемое состояние, перечисление действий и тело редуктора. Всё, что относится к компоненту, находится в одном типе, благодаря чему компоненты TCA можно составлять и удобно проверять при просмотре кода.

Такая единообразная структура повторяется в каждом компоненте приложения.

import ComposableArchitecture

@Reducer
struct CounterFeature {
    @ObservableState
    struct State: Equatable { var count = 0 }
    enum Action { case incrementTapped, decrementTapped }
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .incrementTapped: state.count += 1; return .none
            case .decrementTapped: state.count -= 1; return .none
            }
        }
    }
}

Быстрая проверка: три составляющие

Вспомните роли State, Action и Reducer в TCA.

Итоги: State, Action и Reducer

Вы изучили основу компонента TCA:

  • @Reducer определяет тип компонента, содержащий всё необходимое в одном месте.
  • @ObservableState для структуры хранит данные компонента и обеспечивает точное наблюдение.
  • Перечисление Action содержит все события, при необходимости с ассоциированными значениями.
  • Тело редуктора body использует Reduce, чтобы изменять состояние и возвращать Effect, а при отсутствии побочного эффекта — .none.
  • Состояние изменяет только редуктор, благодаря чему поток данных остаётся предсказуемым.

Далее вы подключите этот компонент к SwiftUI с помощью хранилища.

import ComposableArchitecture

// Recap: @Reducer + State + Action + body(Reduce)
// One type, one source of truth, predictable updates.

Часто задаваемые вопросы

Урок «Состояние, действие и редьюсер» бесплатный?

Да — полный текст урока «Состояние, действие и редьюсер» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.

Чему я научусь в уроке «Состояние, действие и редьюсер»?

Моделируйте возможности приложения как чистые редьюсеры, работающие с состоянием. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Swift Academy?

Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Состояние, действие и редьюсер»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Swift Academy?

Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Состояние, действие и редьюсер
  2. Хранилище и интеграция со SwiftUI
  3. Эффекты и зависимости
  4. Композиция и тестирование возможностей
← Назад к Swift Academy