Timerを毎正時でfireさせる方法

Webで探しても意外と見つからないので、メモ

Timerは、周期的にfireするのは得意

Timerに用意されているinitializerを見ても、いろいろと用意されているのがわかると思います。
例えば次のコードは、60秒ごとに指定したセレクタを呼び出すタイマーの設定です。(自動でスタートします)

周期的タイマー例

いま、時計アプリを作っていて、毎正時等にチャイムを鳴らしたいと考えています。
上のコードは、作成したタイミングでスタートし始めますので、午後1時ぴったり!等にfireさせるようにすることはできません。

Timerクラスを調べてみると、以下のinitializerが使えそうです。

使えそうInitializer

第1引数を見てもわかるようにまずは、fireさせたいDateをきちんと作ることが必要です。
時間を扱うには、SwiftDateが便利ですので、以下のようなコードで、ちょうどX時というような時間を作ります。

コード

上のコードでは、現在のリージョン時間を取得してから、それを60分単位で切り上げています。切り下げると過去になってしまうので切り上げて、次のTimerのfire時間とできるよう準備しています。
最後に、DateInRegionからDateに変換して、Timerのinitializerに渡す準備をしています。

DateInRegion
大抵の国ではUTCからの時差は、1時間単位ですので、DateInRegionを使わずに時間を生成してもあまり問題ありません。例えば、日本は、UTC+9ですので、ローカル時間を使っても、UTC時間を使っても不都合はありません。
ただ、Regionによっては、.5の単位で時差があるケースがあります。例えば、インドは、UTC+5:30なので、ローカル時間を使うときちんと「X時ちょうど」にできますが、UTCを使ってしまうと、「X時30分ちょうど」になってしまいます。ですので、ここでは、DateInRegionを使って、Regionの時間をRegionの単位で切り上げることが必要になっています。
# アプリがどのRegionで使われるかは予想できませんが、このようなベースの部分は、最初からある程度検討しておいた方が、後々のメンテナンスの手間が減ります。

あとは、Timerを初期化して、RunLoopにaddすれば動き出します。

Timer使用例

2番目の引数には、秒で渡す必要があるので、60秒*60分を渡しています。
4番目の引数には、TimerがfireされたときにじっこうされるBlockを渡す必要があります。上の例では、省略していますが、引数としては、Timerが渡されるので、時間に応じて異なる動作をさせる時に情報を使うことができます。

Timerが動かない時
よくハマるのが、RunLoopにaddをし忘れることです。ちゃんとaddしましょう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です