珈琲とバイクとときどきプログラミング

狐が好きな編入生的な人間のブログです

RxSwift + UIRefreshControl + MVVMで幸せになる

iOSにはUIRefreshControlというものがあります(twitterでいうところのシュッテしてポンってなるやつ)

あれ結構使うわりにはいちいちpropertyを設定するのが面倒だったので

MVVMを使うこと前提にRxRefreshingControlというものを作ってしまえば楽じゃねと思い作りました。

import RxSwift
import RxCocoa

protocol RefreshableModel: class {
    func refresh()
    var refreshingState: Driver<Bool> { get }
}

class RxRefreshControl: UIRefreshControl {
    
    private weak var refreshableModel: RefreshableModel?
    private let disposeBag = DisposeBag()
    
    private override init() {
        super.init()
        
        rx.controlEvent(.valueChanged)
            .asDriver()
            .drive(onNext: { [weak self] _ in
                self?.refreshableModel?.refresh()
        }).disposed(by: disposeBag)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init coder has not been implemented")
    }
    
    convenience init(_ refreshableModel: RefreshableModel) {
        self.init()
        self.refreshableModel = refreshableModel
    }
    
    func setupEndRefresingListener(_ listner: @escaping () -> Void) {
        refreshableModel?.refreshingState
                        .drive(onNext: { [weak self] _ in
                            self?.endRefreshing()
                            listner()
                        }).disposed(by: disposeBag)
    }
}

refreshingControlの役割としてはシュってしたらrefreshのリクエストが走り、responseが会ったらポンってなってほしいだけです。

そのため、VMをRefreshableModelに準拠させ、①refreshの状態をDriverによって流してやること、②refresh時APIを叩くメソッド を用意しといて上げれば

ViewController では

private lazy var refreshControl: RxRefreshControl = {
  return RxRefreshControl(viewModel)
}()

としてrefreshControlをしてrefreshしたいtableViewcollectionViewに対して.addSubView(refreshControl)して

もしrefresh時のコールバックを仕込みたければviewDidLoadなどで

refreshControl.setupEndRefreshingListener {
 // 任意のコールバック
}

とすれば良くなるわけですね。

これでrefreshが少しは扱いやすくなるかと思います