RxSwift implements countdown

Use RxSwift to realize the countdown, need to rely on takeWhile to judge the true condition, otherwise the sequence will keep on timing, the main code is as follows a big pile

viewModel.getSMScodeAction.elements.observeOn(MainScheduler.instance).subscribe(onNext: { [weak self](model) in
                guard let self = self else { return }
            self.viewModel.getSMSEableObserable.accept(false)
            let mapResult:Observable<Int> =  Observable<Int>.interval(1, scheduler: MainScheduler.instance).map { (second) -> Int in
                return TOTAL_SECOND - second
            }
            mapResult.takeWhile {
                $0 > 0
            }.subscribe(onNext: { [weak self](remind) in
                guard let self = self else { return }
                self.smsBtn.setTitle("\(remind)s".for: .normal)
                }, onCompleted: {
                    self.smsBtn.setTitle("Get it again".for: .normal)
                    self.viewModel.getSMSEableObserable.accept(true)
            }).disposed(by: self.disposeBag)
            
            
            }, onError: { (error) in
            
            }).disposed(by: disposeBag)
        
Copy the code

explain

Action

The viewModel. GetSMScodeAction. Elements. ObserveOn (MainScheduler. Instance returns a Obserable, on the main thread to subscribe to it, get is, click to get verification code, The successful closure {} for executing the network request is where the countdown is implemented. The viewModel getSMScodeAction is an Action the Action is such a class

public final class Action<Input, Element>
Copy the code

An input, and an Element, in which elements is an Observable of the Action

public let elements: Observable<Element>
Copy the code

The map, takeWhile

Observable

. Interval (1, scheduler: mainScheduler.instance) is an Observable with an interval of 1 that uses map to convert to the remaining time. The type stays the same and the meaning changes

If the countdown time is greater than 0, the mapResult will be executed. If the countdown time is greater than 0, the mapResult will be executed

  mapResult.takeWhile {
                $0 > 0
            }.subscribe(onNext: { [weak self](remind) in
                guard let self = self else { return }
                self.smsBtn.setTitle("\(remind)s".for: .normal)
                }, onCompleted: {
                    self.smsBtn.setTitle("Get it again".for: .normal)
                    self.viewModel.getSMSEableObserable.accept(true)
            }).disposed(by: self.disposeBag)
Copy the code

Pay attention to

When the conditions of the takeWhile are not met, it terminates and goes onCompleted{} without skipping the current value

For example,

let disposeBag = DisposeBag()

Observable.of(1, 2, 3, 4, 3, 2, 1)
    .takeWhile { $0 < 4 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

Copy the code

If I get to the first 1,2,3, I stop and I don’t output anything after 4