はじめに
タイマーをセットして時間経過後に一度きりの自動発火処理をRxSwift
を使って実装しました。
環境
Xcode 13.3
Swift 5.6
RxSwift 6.2.0
実装方法の確認
👇の公式ドキュメントを確認しました。
interval(_ period: RxTimeInterval, scheduler: SchedulerType)
interval
を使うことで自動発火の処理が簡単に実現できそうですが、これだと継続的に発火処理が実行されるようです。
Timer.swift
extension ObservableType where Element: RxAbstractInteger {
/**
指定されたスケジューラを使ってタイマーを実行し、オブザーバー・メッセージを送信することで、各期間の後に値を生成する観測可能なシーケンスを返します。
- パラメータ period: 結果のシーケンスに含まれる値を生成する期間。
- パラメータ scheduler: タイマーを実行するスケジューラ。
- 戻り値 各期間の後に値を生成する観測可能なシーケンス。
*/
public static func interval(_ period: RxTimeInterval, scheduler: SchedulerType)
-> Observable<Element> {
return Timer(
dueTime: period,
period: period,
scheduler: scheduler
)
}
}
timer(_ dueTime: RxTimeInterval, period: RxTimeInterval? = nil, scheduler: SchedulerType)
timer
を使うことで一度きりの自動発火の処理が実現できそうです。ただし、period
のパラメータを指定した場合は、継続的に自動発火されてしまうのでdueTime
のみの指定で使っていきます。
Timer.swift
extension ObservableType where Element: RxAbstractInteger {
/**
指定された相対的な初期期日が経過した後、指定されたスケジューラでタイマを実行し、定期的に値を生成する観測可能なシーケンスを返します。
- パラメータ dueTime: 最初の値を生成する相対時間。
- パラメータ period: 後続の値を生成する期間。
- パラメータ scheduler: タイマーを実行するスケジューラ。
- リターン。時間経過後、期間ごとに値を生成する観測可能なシーケンス。
*/
public static func timer(_ dueTime: RxTimeInterval, period: RxTimeInterval? = nil, scheduler: SchedulerType)
-> Observable<Element> {
return Timer(
dueTime: dueTime,
period: period,
scheduler: scheduler
)
}
}
上のメソッド内で返しているTimer
の中は👇
Timer.swift
final private class Timer<Element: RxAbstractInteger>: Producer<Element> {
fileprivate let scheduler: SchedulerType
fileprivate let dueTime: RxTimeInterval
fileprivate let period: RxTimeInterval?
init(dueTime: RxTimeInterval, period: RxTimeInterval?, scheduler: SchedulerType) {
self.scheduler = scheduler
self.dueTime = dueTime
self.period = period
}
override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
if self.period != nil {
let sink = TimerSink(parent: self, observer: observer, cancel: cancel)
let subscription = sink.run()
return (sink: sink, subscription: subscription)
}
else {
let sink = TimerOneOffSink(parent: self, observer: observer, cancel: cancel)
let subscription = sink.run()
return (sink: sink, subscription: subscription)
}
}
}
以上より、今回はObservableType
のtimer
を使ってObservableを生成、それをsubscribeしていこうと思います。
実装
GameViewModel
playModeAutoisSet.subscribe(onNext: { [weak self] time in
guard let self = self else { return }
if let timer = self.autoPlayModeTimer {
timer.dispose()
}
self.showMessage.onNext(String(time) + ALERT_AUTO_PLAY_MODE_ON)
// ここから
self.autoPlayModeTimer = Observable<Int>
.timer(DispatchTimeInterval.seconds(time * 60), scheduler: MainScheduler.instance)
.subscribe { _ in
// ここに処理を書く
if self.playModeAuto.value != false {
self.playModeAuto.accept(false)
self.playModeB.accept(true)
self.playModeBisChanged.onNext(true)
self.showMessage.onNext(ALERT_AUTO_PLAY_MODE_CHANGED)
}
}
// ここまで
}).disposed(by: disposeBag)
これで動作確認したところ、無事一度きりの自動発火が実現できました👏
おわりに
簡単なことでしたが、公式ドキュメントを確認することで少し理解が深まった気がします。
参考