概要
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が更新される
ソースコードはこちらです。
参考リンク