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

全体のソースコードはこちらです。
Reduxアーキテクチャの登場人物
Reduxの登場人物を紹介します。
View
画面表示・ユーザーイベントをハンドリングします。
State
アプリケーションの状態を管理します。
Action
Stateをどのように変更するか表現します。
Reducer
StateとActionから新たなStateを生み出します。
Store
StateとReducerを内包します。
Actionのディスパッチを担当します。
Reduxの流れ
主に以下の流れでアプリケーションの状態を更新します。
- Viewでイベントが発生する
- イベントをトリガーに
Store
がAction
をディスパッチする -
Reducer
がディスパッチされたAction
と既存のState
から新しいState
を生成する -
State
を監視しているView
が更新される
カウンターアプリを作る
Reduxを使った実装を体験してみます。
プロジェクトを作成する
Xcodeで新規プロジェクトを作ります。
その後、Swift Package MangerでReSwiftをインストールします。
State
を実装する
カウンターアプリなので数字を状態管理します。
struct CounterState {
var number: Int = 0
}
Action
を実装する
数字を一つ増やすAction
と数字を一つ減らすAction
を準備します。
extension CounterState {
enum Action: ReSwift.Action {
case increment
case decrement
}
}
Reducer
を実装する
Action
と既存のState
から新しいState
を生成します。
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
と名づけることが多いです。
struct AppState {
var counterState = CounterState()
}
AppState
と対応するReducer
を実装する
AppState
に対応するReducer
を実装します。
発生したAction
を下位のReducer
に渡します。
今回の場合は appReducer -> CounterState.reducer
となります。
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
は以下の特徴を持ちます。
- アプリケーションに一つ
-
State
,Reducer
を保持する -
Action
のディスパッチを担当する
AppDelegate
で生成しましょう。
import UIKit
import ReSwift
let appStore = ReSwift.Store<AppState>(reducer: appReducer, state: nil)
...
ボタンタップ時にAction
をディスパッチする
ボタンタップ時にディスパッチして、新しいState
が生成されるようにします。
また、Store
がViewのイベントを監視できるようにします。
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のデータフローを実現できました。
- Viewでイベントが発生する
- StoreがActionをディスパッチする
- ReducerがActionを解析して、新しいStateを生成する
- 新しいStateを元にViewが更新される
ソースコードはこちらです。
参考リンク