Rxswiftを使ってソートボタンを作る


ソートボタンを作った経緯

今回はRxSwiftを使ってソートボタンを作ったのですが、作るのに時間がかかってしまったため他にもソート機能の作り方に悩んでいる方がいれば参考になればと思い書きました。

ソートする値

今回はapiで取ってきたmodelにあるデータに対してソートする機能を作成しました。
startedAtが取得したデータの登録された日付なので、そちらを昇順にしたいと思います。
値のjsonの型としては下記になります。

struct ConnpassStruct: Codable {
    var events: [Events]
    struct Events: Codable {
        var title: String
        var eventUrl: String
        var startedAt: String
        private enum CodingKeys: String, CodingKey {
            case title
            case eventUrl = "event_url"
            case startedAt = "started_at"
        }
    }
}

BehaviorRelay

今回はデータを保持したいのでBehaviorRelayを使いました。
BehaviorRelayにて保持したデータを、ソートボタンのイベントを取得したら
sortメソッドを使って、昇順にします。

self.ascButton.drive(onNext: { _ in
    let ascDate = self.resultsfields.value.sorted(by: {$0.startedAt < $1.startedAt})
    self.resultsfields.accept(ascDate)
}).disposed(by: disposeBag)

参考

RxSwift 再入門
RxCocoa 4 の Signal と Relay のまとめ

Drive

modelで取得したデータをViewModelでsortし、ViewにDriverを使ってbindさせています。
コードとしては下記になります。

viewcontroller.swift
var ascButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .fastForward, target: nil, action: nil)

//省略

self.viewModel = ConnpassViewModel(
ascButton: self.ascButton.rx.tap.asDriver(onErrorDriveWith: Driver.empty()),
)

    self.viewModel.ascButton
        .drive(onNext: { [weak self] in
            self?.tableView.reloadData()
        }).disposed(by: disposeBag)
viewmodel.swift
class ViewModel {
    let ascButton: Driver<Void>
    //ここはmodelで取ってきたデータをresultsfieldsにいれているがその記述は省略しています
    var resultsfields = BehaviorRelay<[ConnpassStruct.Events]>(value: [])
    private let disposeBag = DisposeBag()
    init(ascButton: Driver<Void>) {
        self.ascButton.drive(onNext: { _ in
            let ascDate = self.resultsfields.value.sorted(by: {$0.startedAt < $1.startedAt})
            self.resultsfields.accept(ascDate)
        }).disposed(by: disposeBag)
    }
}

参考

RxSwiftでの実装練習の記録ノート(後編)
RxExample MVVM のその先へ(Fat ViewModel の倒し方)