0Pricing
Swift Academy · レッスン

StoreとSwiftUIの統合

TCAのStoreからViewを駆動します。

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

Storeとは

Store は、機能のStateを保持し、そのReducerを実行する実行時オブジェクトです。SwiftUIのViewはStoreを監視してStateを読み取り、Storeにアクションを送信して変更を発生させます。

Storeは、純粋なReducerロジックと実際に動作するインタラクティブなUIをつなぐ橋渡し役です。

import ComposableArchitecture
import SwiftUI

// A Store holds State, runs the Reducer,
// and is observed by SwiftUI views.

StoreOf型エイリアス

StoreOf<Feature> は、特定の機能のState型とAction型に特化したStoreを表す便利なエイリアスです。Viewでは通常、これを保存プロパティとして1つ保持します。

つまり、カウンターのViewは StoreOf<CounterFeature> を保持します。

import ComposableArchitecture
import SwiftUI

struct CounterView: View {
    let store: StoreOf<CounterFeature>
    var body: some View {
        Text("Count: \(store.count)")
    }
}

Storeの作成

Store のイニシャライザーに初期Stateと実行するReducerを渡して、Storeを作成します。通常はアプリのエントリーポイント付近で一度だけ行います。

末尾クロージャは、機能のインスタンスを返します。

import ComposableArchitecture
import SwiftUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            CounterView(
                store: Store(initialState: CounterFeature.State()) {
                    CounterFeature()
                }
            )
        }
    }
}

Stateを直接読み取る

最新の監視機能では、store.count のようにドット記法を使ってStoreからStateを直接読み取ります。Stateが @ObservableState であるため、SwiftUIは変更された値に依存する部分だけを再レンダリングします。

単純な読み取りに追加のラッパーは必要ありません。

import ComposableArchitecture
import SwiftUI

struct CounterView: View {
    let store: StoreOf<CounterFeature>
    var body: some View {
        VStack {
            Text("Count: \(store.count)")
        }
    }
}

Actionの送信

Stateを変更するには、store.send(.someAction) でViewからアクションを送信します。StoreはそのアクションをReducerに渡し、ReducerがStateを変更したり、Effectを開始したりします。

ここでは、ボタンからインクリメントとデクリメントのアクションを送信します。

import ComposableArchitecture
import SwiftUI

struct CounterView: View {
    let store: StoreOf<CounterFeature>
    var body: some View {
        HStack {
            Button("-") { store.send(.decrementTapped) }
            Text("\(store.count)")
            Button("+") { store.send(.incrementTapped) }
        }
    }
}

Storeからのバインディング

双方向バインディングが必要なSwiftUIのコントロールに対して、TCAではStateがバインディング可能なアクションを使っている場合に、Storeから Binding を作成できます。コントロールはバインディングアクションを送信してStateを更新します。

ここでは、TextFieldをnameフィールドにバインドします。

import ComposableArchitecture
import SwiftUI

struct FormView: View {
    @Bindable var store: StoreOf<FormFeature>
    var body: some View {
        TextField("Name", text: $store.name)
    }
}

従来のWithViewStore

監視機能が導入される前は、ViewはUIを WithViewStore でラップし、Stateを読み取ったり、アクションを送信するための viewStore を取得したりしていました。古いコードでは今でもこの形式を見かけます。

observe クロージャでは、Viewが依存するStateを選択します。

import ComposableArchitecture
import SwiftUI

struct LegacyCounter: View {
    let store: StoreOf<CounterFeature>
    var body: some View {
        WithViewStore(store, observe: { $0 }) { viewStore in
            Text("Count: \(viewStore.count)")
        }
    }
}

監視対象の選択

従来のスタイルでは、observe クロージャでViewが監視するStateの範囲を絞り込むため、その値が変更された場合にだけ再レンダリングされます。State全体を監視するのが最も簡単ですが、一部だけを監視すると大きなViewを最適化できます。

最新の @ObservableState ではこれが自動的に処理されますが、古いコードを読むときにはobserveの仕組みを理解しておくと役立ちます。

import ComposableArchitecture
import SwiftUI

// Observe just the count to limit re-renders:
WithViewStore(store, observe: { $0.count }) { viewStore in
    Text("\(viewStore)")
}

ペイロード付きActionの送信

関連値を持つActionを送信するには、その値を渡します。これは、Reducerが必要とするユーザー入力をViewから渡す方法です。

ここでは、行を選択すると選択したidを送信します。

import ComposableArchitecture
import SwiftUI

struct ListView: View {
    let store: StoreOf<ListFeature>
    var body: some View {
        Button("Pick 42") {
            store.send(.rowSelected(id: 42))
        }
    }
}

Actionのアニメーション

送信するActionにアニメーションを付けると、その結果として発生するStateの変更をアニメーション表示できます。send に animation 引数を渡します。

これにより、Stateは単純なデータのまま、アニメーションに関する処理を呼び出し側に置けます。

import ComposableArchitecture
import SwiftUI

Button("Toggle") {
    store.send(.toggleTapped, animation: .default)
}

Viewを組み立てる

完全なTCAのViewは StoreOf を保持し、ドット記法でStateを読み取り、コントロールからActionを送信します。残りの処理はReducerが担うため、Viewを薄く宣言的に保てます。

この分離により、TCAのViewは読みやすく、プレビューもしやすくなります。

import ComposableArchitecture
import SwiftUI

struct CounterView: View {
    let store: StoreOf<CounterFeature>
    var body: some View {
        VStack {
            Text("Count: \(store.count)")
            Button("Increment") { store.send(.incrementTapped) }
        }
    }
}

クイックチェック:UIの駆動

SwiftUIのViewがTCAでStateの変更を発生させる方法を思い出しましょう。

まとめ:StoreとSwiftUI

機能をSwiftUIに接続しました。

  • StoreOf<Feature> はStateを保持し、Reducerを実行します。
  • 初期Stateと機能のReducerを指定してStoreを作成します。
  • 最新の監視機能により、store.count のようなドット記法でStateを読み取れます。
  • Viewは store.send(.action) を呼び出してStateを変更します。必要に応じてペイロードやアニメーションも指定できます。
  • 古いコードでは、従来の WithViewStore とその observe クロージャが使われています。

次はReducerに副作用と注入可能な依存関係を追加します。

import ComposableArchitecture
import SwiftUI

// Recap: StoreOf holds state, view reads with dot syntax,
// store.send(.action) drives every change.

よくある質問

「StoreとSwiftUIの統合」レッスンは無料ですか?

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

「StoreとSwiftUIの統合」で何を学びますか?

TCAのStoreからViewを駆動します。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

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

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

「StoreとSwiftUIの統合」レッスンにはどのくらい時間がかかりますか?

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

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

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

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

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