0Pricing
Swift Academy · レッスン

State、Action、Reducer

機能を、Stateに対する純粋なReducerとしてモデル化します。

「State、Action、Reducer」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。

Composable Architectureとは

Composable Architecture(TCA)は、一貫性があり、テスト可能な構造でSwiftアプリを構築するためのライブラリです。中心となるのは3つの要素です。機能のデータを表すState、発生し得るすべてのイベントを表すAction、そしてアクションに応じてStateを変化させるReducerです。

この一方向のデータフローにより、機能の動作が予測しやすく、テストも簡単になります。

import ComposableArchitecture

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

@Reducerマクロ

機能は、@Reducerマクロでアノテーションを付けた型として定義します。このマクロによって補助コードが生成され、State、Action、Reducerの body を1つのまとまりとして一緒に宣言できます。

慣例として、型には機能にちなんだ名前を付けます。たとえば CounterFeature のようにします。

import ComposableArchitecture

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

Stateの定義

State は通常、機能に必要なすべてのデータを保持するstructです。@ObservableState を付けると、SwiftUIが変更を正確に監視し、影響を受ける部分だけを再レンダリングできるようになります。

Stateは最小限に保ち、派生値は計算して求めることで、信頼できる唯一の情報源を維持します。

import ComposableArchitecture

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

Actionの定義

Action は、ユーザーのタップ、レスポンス、タイマーのティックなど、機能が処理できるすべてのイベントを列挙するenumです。各caseはStateを直接変更する命令ではなく、発生した出来事を表します。

分かりやすいアクション名にすると、機能の動作がコード自体で説明されます。

import ComposableArchitecture

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

    enum Action {
        case incrementTapped
        case decrementTapped
    }
}

Reducerのbody

Reducerのロジックは、some ReducerOf<Self> を返す計算プロパティ body に記述します。その中で Reduce を使い、変更可能な state と受け取った action を受け取るクロージャを定義します。

アクションをswitch文で分岐し、それに応じてStateを変更します。

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の返却

Reducerの各分岐では、Effect を返す必要があります。.none を返すと、Stateの変更後に実行する非同期処理がないことを表します。

ネットワーク呼び出しなどの副作用が必要な場合は、その処理を実行するEffectを返します。Effectについては後のレッスンで詳しく学びます。

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
}

Stateの変更を一元化する

TCAの基本ルールは、Stateを変更できるのはReducerだけであり、変更はアクションへの応答としてのみ行うということです。ViewがStateを直接変更することはありません。Viewはアクションを送信します。

このように変更処理を1か所に集約することで、データフローを追跡しやすく、テストもしやすくなります。

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のコントロールから型安全にアクションを通して更新できるようになります。

ここでは、テキストフィールドとトグルを機能のStateで管理します。

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()
    }
}

関連値を持つAction

Actionは、関連値を使ってデータを保持できます。読み込んだアイテムやエラーなど、処理結果をモデル化するときに使います。これにより、Reducerはその値をStateに保存できます。

ここでは、レスポンスアクションが取得した数値を保持します。

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
            }
        }
    }
}

EquatableなStateが役立つ理由

State を Equatable に準拠させると、TCAはStateが実際に変更されたかどうかを検出できます。これにより、Viewの正確な更新と網羅的なテストが可能になります。小さな要件ですが、大きなメリットがあります。

保存するプロパティがすでに Equatable であれば、多くのState型は追加の処理なしで Equatable に準拠できます。

import ComposableArchitecture

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

完成した機能の構成

完成した機能は、上から順に読むと、マクロ、監視可能なState、アクションenum、Reducerのbodyで構成されています。機能に関するすべての要素が1つの型に収まるため、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
            }
        }
    }
}

クイックチェック:3つの要素

TCAにおけるState、Action、Reducerの役割を思い出しましょう。

まとめ:State、Action、Reducer

TCAの機能の中心となる要素を学びました。

  • @Reducer は、すべての要素を1か所に含む機能の型を定義します。
  • structに @ObservableState を付けると、機能のデータを保持し、正確な監視が可能になります。
  • Action enumには、関連値を持たせることもできる、すべてのイベントを列挙します。
  • Reducerの body は Reduce を使ってStateを変更し、Effect を返します。副作用が不要な場合は .none を返します。
  • Stateを変更できるのはReducerだけなので、データフローを予測しやすく保てます。

次はStoreを使って、この機能をSwiftUIに接続します。

import ComposableArchitecture

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

よくある質問

「State、Action、Reducer」レッスンは無料ですか?

はい。「State、Action、Reducer」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。

「State、Action、Reducer」で何を学びますか?

機能を、Stateに対する純粋なReducerとしてモデル化します。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「State、Action、Reducer」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. State、Action、Reducer
  2. StoreとSwiftUIの統合
  3. Effectと依存関係
  4. 機能の合成とテスト
← Swift Academyに戻る