LoginSignup
3
2

More than 1 year has passed since last update.

最小単位でFluxアーキテクチャを試す

Posted at

概要

Fluxアーキテクチャを最小単位で実装してみます。
今回はRxSwiftを用いてFluxのサンプルを実装します。

環境

Xcode 13.1
以下の内容はXcodeのPlayground環境を使ってます。
環境を作るときは以下のリンクを参考にしました。

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

以下にFluxアーキテクチャの登場人物を簡単に紹介します。

View

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

Store

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

Action

Storeを変更できる唯一の存在です。

Dispatcher

Actionは必ずDispatcherを通してStoreへ渡されます。
具体的にはDispatcherにdispatchというメソッドを実装し、
Actionを渡せるようにします。

Fluxアーキテクチャとは?

2014年にFacebookが発表したアーキテクチャです。
特徴はデータの流れが単一方向であることです。
データは以下のような流れをたどります。
1. Viewでイベントが発生する
2. イベントをトリガーにActionCreatorがActionを作成する
3. Dispatcherを通してActionがStoreへ渡される
4. 渡されたActionをStoreが解釈して自身を更新する
5. Storeを監視しているViewが更新される

サンプルを作成する

実際にサンプルを作ってみます。

Storeを実装する

アプリケーションの状態は一つなのでシングルトンで実装します。

FluxPlayground
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で表現します。

FluxPlayground
enum Action {
    case updateState(Int)
}

Dispatcherを実装する

二つの関数を実装します。
1. StoreActionを登録するための関数 register(callback:)
2. Actionを実行するための関数 dispatch()

FluxPlayground
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)
    }
}

StoreActionを登録する

Actionに応じてStoreが更新されるように初期化処理を記述します。

FluxPlayground
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)
            }
        }
    }
}

全体のコードです。

FluxPlayground
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をディスパッチすることで状態が更新されることが確認できました。

参考リンク

3
2
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
3
2