RxSwiftでBindTo可能な独自プロパティを生やす方法

MVVM的なアーキテクチャを採用しようとしたとき,KRProgressHUD のような簡単にLoadingViewを出せるライブラリを使っていると,そのViewのOn/Offの制御をどうしようかと少し悩みます。ViewModelのOutputを普通にSubscribeして制御しても良いですが,できればRxCocoaで拡張されたUIView同様,BindToで見た目を制御したいです。

そこで,そもそもBindToをどうやって実現しているのか本家ソースコードを除くと一目瞭然で,Binder というコンポーネントを利用すれば良いことがわかりました。ObservableのbindにBinderを渡してやることで他のUIViewと同様,自動的にsubscribeしてくれます。

具体的な使い方として,たとえばUIViewControllerに以下のようなextensionを定義してあげると良さげです。 以下の例におけるComputedPropertyのloadingViewはBindTo可能なプロパティを定義している本家コードほぼそのまま持ってきています。 第一引数で渡したインスタンスが,クロージャの第一引数へ,クロージャの第二引数に渡ってくる値が入っています。

extension UIViewController {
    
    func showLoadingView() {
        KRProgressHUD.show()
    }
    
    func hideLoadingView() {
        KRProgressHUD.dismiss()
    }
    
    var loadingView: Binder<Bool> {
        return Binder(self) {(vc, value: Bool) in
            value ? vc.showLoadingView() : vc.hideLoadingView()
        }
    }
}

これで,なんの違和感もなくBindできました。

class TestViewController: UIViewController {
    
    private let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // ローディングViewを一定間隔でOnOffさせる。
        Observable<Int>
            .interval(1.0, scheduler: MainScheduler.instance)
            .scan(true) { current, _ in !current }
            .take(3)
            .bind(to: self.loadingView)
            .disposed(by: self.disposeBag)
    }
}

ちなみにUILabelのその他のプロパティも本家の定義と同様にextensionを定義してやることで,Bindableにすることができます。