📖

【学習備忘録】RxSwiftを使ったCounterアプリ

2022/05/18に公開

はじめに

MVVMのアーキテクチャを学習するにあたり、まずはRxSwiftについて学習しています。
「比較して学ぶ RxSwift4 入門」のCounterアプリのコードをもとに学習しました。

RxSwiftとは

RxSwift は「非同期イベントを受け取るための枠組み」を提供してくれるライブラリです。
・UI イベント受け取り
・Web API レスポンス受け取り
・データの変化の監視
RxSwift は ReactiveX ファミリーのひとつです。ReactiveX には、C#、Java など様々な言語のライブラリがあります。

引用:RxSwift 再入門

メリット

  • 時間経過に関する処理をシンプルに書ける • コード全体が一貫する
  • まとまった流れが見やすい
  • 差分がわかりやすい
  • 処理スレッドを変えやすい • callback を減らせる
    – インデントの浅いコードに
    このようなメリットがあります。

サンプルコード

完成

1. 構造体(struct)にカウントアップ・カウントダウン・カウントリセットボタンをObservable(観測可能)であることを定義する。

  • ボタンを押すイベントを検知できるようにする。
struct CounterViewModelInput {
    let countUpButton: Observable<Void>
    let countDownButton: Observable<Void>
    let countResetButton: Observable<Void>
}

2. 表示するテキストをプロトコルで定義する。

  • counterTextはDriver型であることを示す。
    Driverは、
  • メインスレッドで通知
  • shareReplayLatestWhileConnected を使った Cold-Hot 変換
  • onError 通知しない
    を強制することができます。
protocol CounterViewModelOutput {
    var counterText: Driver<String?> { get }
}

3. InputとOutputをプロトコルで定義する。

protocol CounterViewModelType {
    var outputs: CounterViewModelOutput? { get }
    func setup(input: CounterViewModelInput)
}

4. ViewModelに実装

  • CounterRxViewModelはCounterViewModelTypeに適合。
  • functionでカウントアップ・ダウン・リセットでカウンター機能を実装。
  • setupメソッドでイベントを検知した時に、何をするのか示す。
  • 初期化時にリセットされるようにしておく。
  • extentionでCounterRxViewModelをCounterViewModelOutputに適合させ、textの表示方法を指定し、エラー発生時にはnilが返るようにする。
class CounterRxViewModel: CounterViewModelType {
    var outputs: CounterViewModelOutput?

    private let countRelay = BehaviorRelay<Int>(value: 0)
    private let initialCount = 0
    private let disposeBag = DisposeBag()

    init() {
        self.outputs = self
        resetCount()
    }

    func setup(input: CounterViewModelInput) {
        input.countUpButton
            .subscribe(onNext: { [weak self] in
                self?.incrementCount()
            })
            .disposed(by: disposeBag)

        input.countDownButton
            .subscribe(onNext: { [weak self] in
                self?.decrementCount()
            })
            .disposed(by: disposeBag)

        input.countResetButton
            .subscribe(onNext: { [weak self] in
                self?.resetCount()
            })
            .disposed(by: disposeBag)
    }

    private func incrementCount() {
        let count = countRelay.value + 1
        countRelay.accept(count)
    }

    private func decrementCount() {
        let count = countRelay.value - 1
        countRelay.accept(count)
    }

    private func resetCount() {
        countRelay.accept(initialCount)
    }
}

extension CounterRxViewModel: CounterViewModelOutput {
    var counterText: Driver<String?> {
        return countRelay
        .map { "Rxパターン:\($0)" }
        .asDriver(onErrorJustReturn: nil)
    }
}

5. ViewControllerを実装

  • setUpViewModelのメソッド内に、inputにそれぞれのボタンタップ処理を指定し、outputsにtextを指定する
  • ViewDidLoad()でseyupViewModelを呼び出す。
class ViewController: UIViewController {

    @IBOutlet weak var countLabel: UILabel!
    @IBOutlet weak var countUpBotton: UIButton!
    @IBOutlet weak var countDownButton: UIButton!
    @IBOutlet weak var countResetButton: UIButton!

    private let disposeBag = DisposeBag()

    private var viewModel: CounterRxViewModel!

    override func viewDidLoad() {
        super.viewDidLoad()
        setupViewModel()
    }

    private func setupViewModel() {
        viewModel = CounterRxViewModel()
        let input = CounterViewModelInput(
            countUpButton: countUpBotton.rx.tap.asObservable(),
            countDownButton: countDownButton.rx.tap.asObservable(),
            countResetButton: countResetButton.rx.tap.asObservable()
        )
        viewModel.setup(input: input)

        viewModel.outputs?.counterText
            .drive(countLabel.rx.text)
            .disposed(by: disposeBag)
    }

}

GitHub

https://github.com/MasakatsuTagishi/Practice-RxSwift-CounterApp

参考にしたもの

1.比較して学ぶRxSwift入門

https://www.amazon.co.jp/比較して学ぶRxSwift入門-技術の泉シリーズ(NextPublishing)-髙橋-凌-ebook/dp/B07LGMNXS3

2.RxSwift 再入門

https://qiita.com/usamik26/items/444d6dd7386b2949c06b#:~:text=RxSwift とは,してくれるライブラリです。&text=RxSwift は ReactiveX ファミリーの,言語のライブラリがあります。

3.【RxSwift】BehaviorRelayとPublishRelayについてまとめてみた

https://egg-is-world.com/2018/08/11/rxswift-behaviorrelay-publishrelay/

4.RxCocoaが提供するDriverって何?

https://qiita.com/k5n/items/44ef2ab400f47fb66731

5.ReactiveX/RxSwift

https://github.com/ReactiveX/RxSwift

さいごに

実際に手を動かしてみるとコードを読むだけではわからないところが理解できました。
また、調べながらまとめていくとより理解が深まりました。
しかし、よくわかっていないところも多く、個人開発でもRxSwiftを使って(Combineも使いたいなー...)理解を深めていきたいと思います。
間違いや認識違いがあれば指摘いただければ幸いです。

Discussion