Swift
DispatchTime
Swift programming
concurrency
delay function

Delay using DispatchTime.now float?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you want to delay work in Swift with Grand Central Dispatch, the normal tool is DispatchQueue.asyncAfter. The important detail is that DispatchTime.now() works naturally with DispatchTimeInterval or TimeInterval, while a plain Float often needs conversion first.

So the short answer is: do not build your delay API around Float unless you must. Use TimeInterval, which is a type alias for Double, or convert the Float explicitly before scheduling.

Prefer TimeInterval for Delays

TimeInterval matches the rest of the Foundation ecosystem and makes call sites clearer.

swift
1import Foundation
2
3let semaphore = DispatchSemaphore(value: 0)
4let delay: TimeInterval = 0.5
5
6DispatchQueue.global().asyncAfter(deadline: .now() + delay) {
7    print("Ran after half a second")
8    semaphore.signal()
9}
10
11_ = semaphore.wait(timeout: .now() + 2)

This is the most readable form when you already know the delay in seconds. It also avoids unnecessary precision loss from using Float.

Convert Float Explicitly When Needed

Sometimes a value already arrives as Float, perhaps from a UI control or an older API. In that case, convert it deliberately instead of expecting DispatchTime.now() to understand the Float directly.

One safe option is to convert to milliseconds:

swift
1import Foundation
2
3let semaphore = DispatchSemaphore(value: 0)
4let delaySeconds: Float = 0.75
5let interval = DispatchTimeInterval.milliseconds(Int(delaySeconds * 1000))
6
7DispatchQueue.global().asyncAfter(deadline: .now() + interval) {
8    print("Ran after converted Float delay")
9    semaphore.signal()
10}
11
12_ = semaphore.wait(timeout: .now() + 2)

You can also convert with Double(delaySeconds) if you want to keep the code in seconds, but the key idea is the same: make the conversion explicit.

Pick the Right Queue

Delaying work and choosing where that work runs are separate decisions. If the delayed closure updates UIKit, schedule it on the main queue. If it does background work, use a global or custom queue.

swift
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
    print("Safe place for UI follow-up work")
}

This matters because asyncAfter does not block the queue while waiting. It simply schedules the closure to run later on the queue you chose.

Use Delays for Scheduling, Not for Blocking

A delay with asyncAfter is very different from Thread.sleep. Sleeping blocks the current thread. Scheduling with asyncAfter lets the runtime keep doing other work until the deadline is reached.

That makes asyncAfter the better choice for:

  • UI follow-up actions
  • lightweight retries
  • time-based state changes
  • debounced work

If you are building structured asynchronous code in newer Swift concurrency, Task.sleep can also be a good fit, but that is a different API from Grand Central Dispatch.

Precision and Intent

For user-facing delays such as 0.3 or 1.5 seconds, TimeInterval is usually sufficient and idiomatic. If your code needs integer-based precision for scheduling, DispatchTimeInterval values such as .milliseconds(250) make the intent even clearer.

That often reads better than a raw floating-point literal because the unit is visible at the call site.

Common Pitfalls

The most common mistake is trying to add a Float directly without converting it. Even when the compiler can help, the resulting code is less clear than using TimeInterval or DispatchTimeInterval from the start.

Another issue is scheduling delayed work on the wrong queue. If the closure touches UI state, it belongs on DispatchQueue.main.

It is also easy to confuse delaying with blocking. Thread.sleep pauses the current thread, while asyncAfter schedules future work without blocking the caller.

Finally, avoid representing time with Float unless another API forces it. Double-based time values are the conventional choice in Swift.

Summary

  • Use DispatchQueue.asyncAfter with DispatchTime.now() for delayed execution.
  • Prefer TimeInterval or DispatchTimeInterval instead of raw Float.
  • If you already have a Float, convert it explicitly.
  • Choose the main queue for UI work and a background queue for non-UI work.
  • Treat asyncAfter as scheduling, not as a thread-blocking pause.

Course illustration
Course illustration

All Rights Reserved.