
Swiftで細かなタイミング制御が必要なとき、より低レベルで柔軟なタイマーとして使えるのが DispatchSourceTimer です。
この記事では、DispatchSourceTimer の基本的な意味や使い方、主要な引数の意味、活用シーンをわかりやすく解説します。
DispatchSourceTimer とは?
DispatchSourceTimer は、GCD(Grand Central Dispatch) ベースで動作する高精度なタイマーです。
UIKit や Foundation の Timer よりも 低レベル(OS の仕組みに近い層で直接動作するという意味) で動作するため、バックグラウンドスレッドでも正確に動き、イベント処理やスレッド制御との相性がとても良いのが特徴です。
特に以下のような場面に向いています:
- 高頻度な繰り返し処理
- 柔軟なキャンセルや再スケジューリング
- DispatchQueue と統合したタスク管理
基本の使い方
下記は1秒ごとに処理を繰り返すシンプルな例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import SwiftUI struct ContentView: View { @State private var message = "タイマーは停止中" @State private var isRunning = false @State private var timer: DispatchSourceTimer? var body: some View { VStack(spacing: 20) { Text(message) .font(.headline) .padding() Button(isRunning ? "タイマー停止" : "タイマー開始") { if isRunning { stopTimer() } else { startTimer() } isRunning.toggle() } .buttonStyle(.borderedProminent) } .padding() } private func startTimer() { let queue = DispatchQueue(label: "my.timer.queue") let newTimer = DispatchSource.makeTimerSource(queue: queue) newTimer.schedule(deadline: .now(), repeating: 1.0) newTimer.setEventHandler { DispatchQueue.main.async { message = "1秒ごとの処理: \(Date())" } } newTimer.resume() timer = newTimer } private func stopTimer() { timer?.cancel() timer = nil message = "タイマーは停止中" } } #Preview { ContentView() } |

コードのポイント
@State private var timer: DispatchSourceTimer?
→ SwiftUI の状態としてタイマーを保持します。停止時にnilに戻せます。DispatchQueue(label:)
→ タイマー用の専用スレッド(ディスパッチキュー)を作成。UI スレッドに負荷をかけないようにします。.schedule(deadline: .now(), repeating: 1.0)
→ 「今すぐ開始して、1秒ごとに繰り返す」という設定。.setEventHandler { ... }
→ ここに「1秒ごとに実行したい処理」を書きます。UI 更新が必要な場合はDispatchQueue.main.asyncを使ってメインスレッドに戻します。.resume()
→ 必ず最後に呼ぶ必要あり。呼ばないとタイマーが動作しません。stopTimer()
→timer?.cancel()で確実に止めて、参照をnilに戻してクリーンアップします。
この例では、DispatchQueueを指定し、.schedule() で実行タイミングを設定し、.setEventHandler にタイマーごとに実行したい処理を定義します。
最後に .resume() を忘れず呼び出すことで、タイマーがスタートします。
主要な引数とその意味
DispatchSourceTimer の使い方を理解するためには、主要なメソッドを把握することが大切です。
| メソッド/引数 | 意味 |
|---|---|
makeTimerSource(queue:) |
実行対象のDispatchQueueを指定。通常は専用キューを作る |
schedule(deadline:repeating:leeway:) |
開始タイミング、繰り返し間隔、許容誤差を指定 |
setEventHandler(handler:) |
実行したいクロージャ(処理)を登録 |
resume() |
タイマー開始(※明示的に必要) |
cancel() |
タイマーの停止と破棄 |
特に .resume() を呼び忘れるとタイマーは動かない点に注意が必要です。
DispatchSource 系は作成直後は停止状態になっており、明示的な開始が必要です。
leewayとは?
.schedule(deadline:repeating:leeway:) の leeway は、処理タイミングの「許容範囲(遅れてもよい猶予)」を表す引数です。
- 精度よりも省電力を優先したいとき:
leewayを長めに - 処理タイミングの正確さを重視:
leewayを短く(または.nanoseconds(0))
例えば以下のように指定します:
|
1 2 |
timer?.schedule(deadline: .now(), repeating: .seconds(1), leeway: .milliseconds(100)) |
この場合、1秒ごとにイベントが発火しますが、最大100ミリ秒までの遅延は許容されます。
DispatchQueueとの関係
DispatchSourceTimer は非同期処理に強い GCD(複数の処理を効率よく並列で動かすための仕組み) のタイマーなので、処理がどのスレッド(キュー)で実行されるかが明確です。
- メインスレッドでUIを更新したいとき →
DispatchQueue.main - 並列でバックグラウンド処理したいとき →
DispatchQueue.global()またはDispatchQueue(label:)
SwiftUIで使う場合、UI更新は必ずメインスレッドで行うように注意します。
|
1 2 3 4 5 6 |
timer?.setEventHandler { DispatchQueue.main.async { // UIの更新など } } |
DispatchSourceTimerの活用シーン
より高精度または制御性の高いタイマーが必要な場面で使われます。
具体的には以下のようなケースです。
- バックグラウンドでも正確に動作するタイマーを実装したい
- UIとは独立したロジック処理(サーバー通信、ログ収集など)
- SwiftUIの
.taskやTimer.publishのように RunLoop に依存する仕組みでは遅延しやすい処理 - データ送信や監視イベントのスケジューリング
例えば、SwiftUIでカウントダウンをリアルタイムに更新しつつ、音声再生やネットワーク処理を正確なタイミングで連携したいときなどに便利です。
注意点
DispatchSourceTimer を使う際には以下の点に注意してください。
resume()を忘れるとタイマーは動作しないcancel()したタイマーは再利用できない(再作成が必要)- 強参照ループに注意(クロージャ内で
[weak self]を使う) - タイマーは手動で管理(
@Stateなどに保持する必要あり)
SwiftUIとの組み合わせでは、Viewのライフサイクルに応じて startTimer / stopTimer を onAppear / onDisappear で適切に呼び分ける必要があります。
まとめ
DispatchSourceTimerは、GCDベースで動作する高精度なタイマーを作成するAPIです。
- Foundationの
Timerよりも高精度・高柔軟性 .schedule()でタイミングを細かく制御.resume()で明示的にスタートが必要- キュー(スレッド)を指定できるので処理の流れが明確
- SwiftUIでも活用可能だが、メモリ管理とスレッド制御に注意
Timer では精度や制御が足りないと感じるときに、DispatchSourceTimer はとても頼りになるタイマーです。
OS の仕組みに近いレベルで動くため、処理のタイミングを細かくコントロールでき、バックグラウンド処理や正確さが求められる場面でも安心して使えます。
「もっと正確に」「もっと自由に」時間を扱いたいときは、ぜひ DispatchSourceTimer を活用してみてください。

