Web で探しても意外と見つからなかったのでメモです。
Sponsor Link
目次
タイマーを使います
自アプリでずっと CPU を占有し続けるわけにはいかないので、OS が提供しているタイマーを使用します。
Timer についての Apple のドキュメントは、こちら。
特定時間経過後に 通知 できるのがタイマー
タイマーの基本的機能は、特定時間経過後に 通知(fire) することです。
iPhone のタイマーアプリもこの機能です。
タイマーは、指定時間経過後に 通知(fire) として closure を呼ぶもの、指定セレクタを呼ぶもの、NSInvocation を実行するもの、色々と用意されています。
以下の API は 指定時間後に、closure を呼ばせることができるものです。
init(timeInterval: TimeInterval, repeats: Bool, block: (Timer) -> Void)
TimeInterval は、以下のように定義され、単位は秒です。
typealias TimeInterval = Double
型は Double ですが、0.001 秒以上の精度を持つと説明されています。(おそらく最終的にはハードウェア制約から実精度が決まると思われます)
「30秒後」のようなアラームはこの API で実現できますが、「午後2時ぴったり」 というようなアラームは この API では実現できません。
特定の時間に通知することもできます
タイマーを使うと 指定時間の経過だけではなく、指定の時間に通知(fire) することもできます。
iPhone アプリでは、アラームと呼ばれる機能です。
こちらも、通知手段として複数の方法が用意されていますが、以下の API は、指定時間に、closure が実行されるものです。
init(fire: Date, interval: TimeInterval, repeats: Bool, block: (Timer) -> Void)
1番めの引数 Date に、通知が必要な時間をセットします。以降の引数は先ほどと同じです。繰り返し通知する際の間隔と繰り返しするかどうかを指定します。
タイマーを使うときの注意点
タイマーを作成した後は、RunLoop に add しないとタイマーは動作しません。
if let timer = Timer.init(timeInterval: Double(60*60), repeats: true, block: { _ in }) {
RunLoop.current.add(timer, forMode: .common)
}
指定時間を作成する
毎正時の通知をタイマーで実現するには、最初の通知時間を正しく指定できれば、あとは間隔を指定することで繰り返し通知ができます。
正時を Date で作成する
正時という指定時間を Date で指定することが 標準の Date ではすこし手間です。
SwiftDate という GitHub で公開されているライブラリを使うと非常に便利なメソッドが使えるようになります。
現在から見て次にくる正時を計算するには、以下のような計算で可能となります。
let nextFireDate = DateInRegion(Date(), region: Region.current).dateRoundedAt(.toCeilMins(60)).date
上のコードでは、現在のリージョン時間を取得してから、それを60分単位で切り上げています。(切り下げると過去になってしまいます。)
この Date 算出方法と先ほどの API を組み合わせることで毎正時に通知(fire)するタイマーを設定することが可能となります。
ただ、Regionによっては、.5の単位で時差があるケースがあります。例えば、インドは、UTC+5:30なので、ローカル時間を使うときちんと「X時ちょうど」にできますが、UTCを使ってしまうと、「X時30分ちょうど」になってしまいます。ですので、ここでは、DateInRegionを使って、Regionの時間をRegionの単位で切り上げることが必要になっています。
# アプリがどのRegionで使われるかは予想できませんので、このようなベースの部分は、最初からある程度検討しておいた方が、後々のメンテナンスの手間が減ります。
毎正時になるタイマーを作成する
次に来る正時を指定してタイマーを作成し、RunLoop に add することで 毎正時に通知(fire) されるタイマーが完成します。
let nextFireDate = DateInRegion(Date(), region: Region.current).dateRoundedAt(.toCeilMins(60)).date
if let timer = Timer.init(fire: nextFireDate, interval: Double(60*60), repeats: true, block: { _ in }) {
RunLoop.current.add(timer, forMode: .common)
}
- Timer を使用する
- init(fire: Date, interval: TimeInterval, repeats: Bool, block: (Timer) -> Void) を使って、指定時間から指定周期で通知するタイマーを作る
- SwiftDate を使用すると Region を考慮した正時を算出するのが簡単になります
- interval は、秒指定なので 60 * 60 が1時間を表す
- Tips: 作成したタイマーを RunLoop に add するのを忘れない
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
Sponsor Link