LoginSignup
1
1

More than 1 year has passed since last update.

ReSwiftでReduxアーキテクチャのサンプルを作る

Posted at

概要

Reduxアーキテクチャでカウンターアプリを作ります。
Reduxを体験することが目的です。
動作は下のような感じです。

全体のソースコードはこちらです。

Reduxアーキテクチャの登場人物

Reduxの登場人物を紹介します。

View

画面表示・ユーザーイベントをハンドリングします。

State

アプリケーションの状態を管理します。

Action

Stateをどのように変更するか表現します。

Reducer

StateとActionから新たなStateを生み出します。

Store

StateとReducerを内包します。
Actionのディスパッチを担当します。

Reduxの流れ

主に以下の流れでアプリケーションの状態を更新します。
1. Viewでイベントが発生する
2. イベントをトリガーにStoreActionをディスパッチする
3. ReducerがディスパッチされたActionと既存のStateから新しいStateを生成する
4. Stateを監視しているViewが更新される

カウンターアプリを作る

Reduxを使った実装を体験してみます。

プロジェクトを作成する

Xcodeで新規プロジェクトを作ります。
その後、Swift Package MangerでReSwiftをインストールします。

Stateを実装する

カウンターアプリなので数字を状態管理します。

State/CounterState.swift
struct CounterState {
    var number: Int = 0
}

Actionを実装する

数字を一つ増やすActionと数字を一つ減らすActionを準備します。

State/CounterState.swift
extension CounterState {
    enum Action: ReSwift.Action {
        case increment
        case decrement
    }
}

Reducerを実装する

Actionと既存のStateから新しいStateを生成します。

CounterState.swift
extension CounterState {
    ...
    static func reducer(action: ReSwift.Action, state: CounterState?) -> CounterState {
        var state = state ?? CounterState()

        guard let action = action as? CounterState.Action else { return state }
        switch action {
        case .increment:
            state.number += 1
        case .decrement:
            state.number -= 1
        }

        return state
    }
}

Stateを束ねるAppStateを実装する

Stateのルートに位置するStateを実装します。
AppStateと名づけることが多いです。

AppState.swift
struct AppState {
    var counterState = CounterState()
}

AppStateと対応するReducerを実装する

AppStateに対応するReducerを実装します。
発生したActionを下位のReducerに渡します。
今回の場合は appReducer -> CounterState.reducerとなります。

AppState.swift
func appReducer(action: ReSwift.Action, state: AppState?) -> AppState {
    var state = state ?? AppState()
    state.counterState = CounterState.reducer(action: action, state: state.counterState)
    return state
}

Storeを実装する

Storeは以下の特徴を持ちます。
1. アプリケーションに一つ
2. State, Reducerを保持する
3. Actionのディスパッチを担当する

AppDelegateで生成しましょう。

AppDelegate.swift
import UIKit
import ReSwift

let appStore = ReSwift.Store<AppState>(reducer: appReducer, state: nil)
...

ボタンタップ時にActionをディスパッチする

ボタンタップ時にディスパッチして、新しいStateが生成されるようにします。
また、StoreがViewのイベントを監視できるようにします。

ViewController.swift
import UIKit
import ReSwift

class ViewController: UIViewController, StoreSubscriber {
    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        appStore.subscribe(self)
    }

    func newState(state: AppState) {
        label.text = String(state.counterState.number)
    }

    @IBAction func tapCountUpButton(_ sender: UIButton) {
        appStore.dispatchFunction(CounterState.Action.increment)
    }

    @IBAction func tapCountDownbutton(_ sender: UIButton) {
        appStore.dispatch(CounterState.Action.decrement)
    }
}

Reduxのデータフローを実現できました。
1. Viewでイベントが発生する
2. StoreがActionをディスパッチする
3. ReducerがActionを解析して、新しいStateを生成する
4. 新しいStateを元にViewが更新される

ソースコードはこちらです。

参考リンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1