概要
Fluxアーキテクチャを最小単位で実装してみます。
今回はRxSwift
を用いてFluxのサンプルを実装します。
環境
Xcode 13.1
以下の内容はXcodeのPlayground環境を使ってます。
環境を作るときは以下のリンクを参考にしました。
Fluxアーキテクチャの登場人物
以下にFluxアーキテクチャの登場人物を簡単に紹介します。
View
画面表示とユーザーイベントをハンドリングします。
Store
アプリケーションの状態を管理します。
Action
Storeを変更できる唯一の存在です。
Dispatcher
Actionは必ずDispatcherを通してStoreへ渡されます。
具体的にはDispatcherにdispatchというメソッドを実装し、
Actionを渡せるようにします。
Fluxアーキテクチャとは?
2014年にFacebookが発表したアーキテクチャです。
特徴はデータの流れが単一方向であることです。
データは以下のような流れをたどります。
- Viewでイベントが発生する
- イベントをトリガーにActionCreatorがActionを作成する
- Dispatcherを通してActionがStoreへ渡される
- 渡されたActionをStoreが解釈して自身を更新する
- Storeを監視しているViewが更新される
サンプルを作成する
実際にサンプルを作ってみます。
Store
を実装する
アプリケーションの状態は一つなのでシングルトンで実装します。
import RxSwift
import RxCocoa
final class Store {
static let shared: Store = .init()
private let _number = BehaviorRelay<Int>(value: 0)
var number: Int {
return _number.value
}
var numberObservable: Observable<Int> {
return _number.asObservable()
}
private init() {}
}
Action
を実装する
StoreはActionを解析して自身を更新します。
アプリケーションの規模が大きくになるにつれて種類が増えるため、
enum
で表現します。
enum Action {
case updateState(Int)
}
Dispatcher
を実装する
二つの関数を実装します。
-
Store
にAction
を登録するための関数register(callback:)
-
Action
を実行するための関数dispatch()
final class Dispatcher {
static let shared: Dispatcher = .init()
private let _action = PublishRelay<Action>()
private init() {}
func register(callback: @escaping (Action) -> ()) -> Disposable {
return _action.subscribe(onNext: callback)
}
func dispatch(_ action: Action) {
_action.accept(action)
}
}
Store
にAction
を登録する
Action
に応じてStore
が更新されるように初期化処理を記述します。
final class Store {
static let shared: Store = .init()
private let _number = BehaviorRelay<Int>(value: 0)
var number: Int {
return _number.value
}
var numberObservable: Observable<Int> {
return _number.asObservable()
}
// 以下追加
private init(dispatcher: Dispatcher = .shared) {
dispatcher.register { action in
switch action {
case .updateState(let int):
self._number.accept(int)
}
}
}
}
全体のコードです。
import RxSwift
import RxCocoa
enum Action {
case updateState(Int)
}
final class Store {
static let shared: Store = .init()
private let _number = BehaviorRelay<Int>(value: 0)
var number: Int {
return _number.value
}
private init(dispatcher: Dispatcher = .shared) {
dispatcher.register { action in
switch action {
case .updateState(let int):
self._number.accept(int)
}
}
}
}
final class Dispatcher {
static let shared: Dispatcher = .init()
private let _action = PublishRelay<Action>()
private init() {}
func register(callback: @escaping (Action) -> ()) -> Disposable {
return _action.subscribe(onNext: callback)
}
func dispatch(_ action: Action) {
_action.accept(action)
}
}
print("Initial State: \(Store.shared.number)") // => Initial State: 0
Dispatcher.shared.dispatch(.updateState(2))
print("Next State: \(Store.shared.number)") // => Next State: 2
Store
の初期状態が0です。
その後、Action
をディスパッチすることで状態が更新されることが確認できました。
参考リンク