LoginSignup
1
0

More than 1 year has passed since last update.

【iOS】RxSwiftを使ってMVVMなカウントダウンタイマーの作り方

Last updated at Posted at 2021-10-05

リアクティブなカウントダウンタイマーの作り方

手書きでQiita書いたので誤字脱字あったらお許しください...

ViewController

ViewController.swift

class ViewController: UIViewController {

  @IBOutlet private weak var countLabel: UILabel!
  @IBOutlet private weak var startButton: UIButton!
  @IBOutlet private weak var stopButton: UIButton!

  private var viewModel: ViewModel!
  private let disposeBag = DisposeBag()

  override func viewDidLoad() {  
    super.viewDidLoad()
    setupViewModel()
  } 

  private func setupViewModel() {
    let input = ViewModel.Input(
      startButtonTapped: startButton.rx.tap.asObservable(),
      stopButtonTapped: stopButton.rx.tap.asObservable()
    )
    viewModel = ViewModel(trigger: input)
    // output
    viewModel.output().count
      .map { "あと:\($0)秒" }
      .bind(to: countLabel.rx.text)
      .disposed(by: disposeBag)
  }
}

ViewModel

ViewModel.swift

class ViewModel {

  struct Input {
    var startButtonTapped: Observable<Void>
    var stopButtonTapped: Observable<Void>
  }

  struct Output {
    var count: Observable<Int>
  }
  
  private var input: ViewModel.Input!
  private var output: ViewModel.Output!
  
  private let isTimerActionSubject = PublishSubject<Bool>()
  private let countRelay = BehaviorRelay<Int>(value: 60) // 初期値: 60秒 (任意)

  private let disposeBag = DisposeBag()

  init(trigger: ViewModel.Input) {
    input = trigger
    output = ViewModel.Output(
      count: countRelay.asObservable()
    )

    bind()
    setupTimer()
  }


  // MARK: Actions
  private func bind() {
    // start button tapped
    input.startButtonTapped
      .map { true }
      .bind(to: isTimerActionSubject)
      .disposed(by: disposeBag)

    // stop button tapped
    input.stopButtonTapped
      .map { false }
      .bind(to: isTimerActionSubject)
      .disposed(by: disposeBag)
  }
  
  // MARK: Timer
  private func setupTimer() {
    isTimerActionSubject.asObservable()
            .flatMap { $0 ? Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance) : .empty() }
            .withLatestFrom(countRelay) { $1 - $0 }
            .bind(to: countRelay)
            .disposed(by: disposeBag)
  }

  // MARK: -- OUTPUT
  func output() -> ViewModel.Output {
    return output
  }
  
}

こんな感じで動くかと!!
ぜひ試してみてください😁

参考

RxSwift | GitHub
[iOS] RxSwiftのIntervalオペレータを使用して一定間隔で処理を行う
【iOS】MVVMについて

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0