【RxSwift】残り時間をセットして自動発火させる実装

# はじめに
タイマーをセットして時間経過後に一度きりの自動発火処理を`RxSwift`を使って実装しました。

# 環境
Xcode 13.3
Swift 5.6
RxSwift 6.2.0

# 実装方法の確認

👇の公式ドキュメントを確認しました。

https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Observables/Timer.swift

#### `interval(_ period: RxTimeInterval, scheduler: SchedulerType)`
`interval`を使うことで自動発火の処理が簡単に実現できそうですが、これだと継続的に発火処理が実行されるようです。

```swift: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`のみの指定で使っていきます。

```swift: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`の中は👇

```swift: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していこうと思います。

# 実装

```swift: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)
```

これで動作確認したところ、無事一度きりの自動発火が実現できました👏

# おわりに
簡単なことでしたが、公式ドキュメントを確認することで少し理解が深まった気がします。

# 参考

https://dev.classmethod.jp/articles/ios-periodic-processing-with-rxswift-interval/


https://github.com/ReactiveX/RxSwift/blob/main/Documentation/GettingStarted.md