RxSwift bind(to: ) を使ってIndicatorViewを回す。

# RxSwift Binder を使ってみた

## overview 
RxSwift のBinderでSVProgressHUDを使ってみる。

Rxswift のgit hubによしなに書かれてる、サンプルがあり、slackでもそれを参考にしてくれと記載されている。

https://github.com/ReactiveX/RxSwift

その部分を抜粋し、MVVMで使ってみた。

これまで、show と　dismissを使ってやっていた箇所を簡単に記述することができる。

### Sample code

ViewController が下記である。

```Swift

//
//  ViewController.swift
//  RxDemo
//
//  Created by Shichimitoucarashi on 2018/08/11.
//  Copyright © 2018年 karadanote. All rights reserved.
//

import UIKit
import RxSwift
import RxCocoa
import SHColor
import SVProgressHUD

final class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var label:UILabel!
    @IBOutlet weak var button: UIButton!
    @IBOutlet weak var indicator: UIActivityIndicatorView!
    
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        
        super.viewDidLoad()
        
        let viewModel = ViewModel(tap: button.rx.tap.asObservable())
        
        viewModel.indicator
            .bind(to: SVProgressHUD().shared.rx.isShow)
            .disposed(by: disposeBag)
        
        viewModel.action
            .subscribe()
            .disposed(by: disposeBag)
    }
}

extension Reactive where Base: UIActivityIndicatorView {
    
    var isAnimating :Binder<Bool>{
        return Binder(self.base) { indicator, flg in
            if flg {
                indicator.isHidden = false
                indicator.startAnimating()
            }else{
                indicator.isHidden = true
                indicator.stopAnimating()
            }
        }
    }
}

extension Reactive where Base: SVProgressHUD {
    
    var isShow: Binder<Bool> {
        return Binder(self.base) { sv, flg in
            if flg {
                SVProgressHUD.show()
            }else{
                SVProgressHUD.dismiss()
            }
        }
    }
}

private struct ActivityToken<E> : ObservableConvertibleType, Disposable {
    private let _source: Observable<E>
    private let _dispose: Cancelable
    
    init(source: Observable<E>, disposeAction: @escaping () -> ()) {
        _source = source
        _dispose = Disposables.create(with: disposeAction)
    }
    
    func dispose() {
        _dispose.dispose()
    }
    
    func asObservable() -> Observable<E> {
        return _source
    }
}

/**
 Enables monitoring of sequence computation.
 
 If there is at least one sequence computation in progress, `true` will be sent.
 When all activities complete `false` will be sent.
 */
public class ActivityIndicator : SharedSequenceConvertibleType {
    public typealias E = Bool
    public typealias SharingStrategy = DriverSharingStrategy
    
    private let _lock = NSRecursiveLock()
    private let _relay = BehaviorRelay(value: 0)
    private let _loading: SharedSequence<SharingStrategy, Bool>
    
    public init() {
        _loading = _relay.asDriver()
            .map { $0 > 0 }
            .distinctUntilChanged()
    }
    
    fileprivate func trackActivityOfObservable<O: ObservableConvertibleType>(_ source: O) -> Observable<O.E> {
        return Observable.using({ () -> ActivityToken<O.E> in
            self.increment()
            return ActivityToken(source: source.asObservable(), disposeAction: self.decrement)
        }) { t in
            return t.asObservable()
        }
    }
    
    private func increment() {
        _lock.lock()
        _relay.accept(_relay.value + 1)
        _lock.unlock()
    }
    
    private func decrement() {
        _lock.lock()
        _relay.accept(_relay.value - 1)
        _lock.unlock()
    }
    
    public func asSharedSequence() -> SharedSequence<SharingStrategy, E> {
        return _loading
    }
}

extension ObservableConvertibleType {
    public func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable<E> {
        return activityIndicator.trackActivityOfObservable(self)
    }
}

```

ViewModelを下記に記載する。

```swift

//
//  ViewModel.swift
//  RxDemo
//
//  Created by Shichimitoucarashi on 2018/10/10.
//  Copyright © 2018年 karadanote. All rights reserved.
//

import Foundation
import RxSwift
import RxCocoa

class ViewModel {
    
    let action: Observable<Bool>
    let indicator: Observable<Bool>
    
    init(tap: Observable<Void>) {
        
        let indicator = ActivityIndicator()
        indicator = indicator.asObservable()
        
        action = tap.flatMapLatest { _ in
            return ViewModel.variable.observeOn(MainScheduler.instance)
                .catchErrorJustReturn(false)
                .trackActivity(indicator)
        }
    }
    static var variable: Observable<Bool> {
        return Observable.just(false).delay(1.0, scheduler: MainScheduler.instance)
    }
    
}

```

今回の処理でもっとも重要なのは、trackActivityである、これがtracking してくれる便利な関数である。

下記に記したクラスが有効に働くことで、現在処理が走ってるか、走っていないかの判断を行うことができる。

ActivityIndicator <- このクラスが排他的制御を行なっている、

### overview 

処理が走ったら、インクリメントを行い、そうでない場合はデクリメントを行い、処理がひとつも走ってなければ、

falseを返し、一つでも走ってれば、trueを返すという仕組みであるので、処理が走ってれば、インジケーターを回し、そうでなければ、止めるという処理をワンライナーでかける、

最初はインジケーターをまわすのに、Bool値のプロパティーを作成するのはおかしいと感じましたが、

こういう使い方をするのでれば、これはこれで結構優れたものだと思える。

```swift

private struct ActivityToken<E> : ObservableConvertibleType, Disposable {
    private let _source: Observable<E>
    private let _dispose: Cancelable
    
    init(source: Observable<E>, disposeAction: @escaping () -> ()) {
        _source = source
        _dispose = Disposables.create(with: disposeAction)
    }
    
    func dispose() {
        _dispose.dispose()
    }
    
    func asObservable() -> Observable<E> {
        return _source
    }
}

/**
 Enables monitoring of sequence computation.
 
 If there is at least one sequence computation in progress, `true` will be sent.
 When all activities complete `false` will be sent.
 */
public class ActivityIndicator : SharedSequenceConvertibleType {
    public typealias E = Bool
    public typealias SharingStrategy = DriverSharingStrategy
    
    private let _lock = NSRecursiveLock()
    private let _relay = BehaviorRelay(value: 0)
    private let _loading: SharedSequence<SharingStrategy, Bool>
    
    public init() {
        _loading = _relay.asDriver()
            .map { $0 > 0 }
            .distinctUntilChanged()
    }
    
    fileprivate func trackActivityOfObservable<O: ObservableConvertibleType>(_ source: O) -> Observable<O.E> {
        return Observable.using({ () -> ActivityToken<O.E> in
            self.increment()
            return ActivityToken(source: source.asObservable(), disposeAction: self.decrement)
        }) { t in
            return t.asObservable()
        }
    }
    
    private func increment() {
        _lock.lock()
        _relay.accept(_relay.value + 1)
        _lock.unlock()
    }
    
    private func decrement() {
        _lock.lock()
        _relay.accept(_relay.value - 1)
        _lock.unlock()
    }
    
    public func asSharedSequence() -> SharedSequence<SharingStrategy, E> {
        return _loading
    }
}

extension ObservableConvertibleType {
    public func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable<E> {
        return activityIndicator.trackActivityOfObservable(self)
    }
}
```

上記実装方法に意見があるものはいつでもコメント欄に投稿していただけると幸いである。

ご連絡お待ちしております。


引用元

https://github.com/ReactiveX/RxSwift