Calling a Method After Delay - codepath/ios_guides GitHub Wiki

When prototyping or orchestrating animations, it is sometimes useful to run a method after some delay.

Using DispatchQueue.asyncAfter

The standard pattern in modern Swift is to call DispatchQueue.main.asyncAfter(deadline:execute:) directly. Since the code in the braces is a closure, you have to use self to refer to your class methods and variables.

// Delay for 2 seconds, then run the code between the braces.
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    // This code will run on the main queue after the delay
}

For better readability with non-integer units, you can also use .seconds, .milliseconds, or other DispatchTimeInterval cases:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
    // Runs 500 ms from now
}

Using structured concurrency

From a synchronous context, you can wrap the delay in a Task and call Task.sleep(nanoseconds:) (iOS 13+). Unlike Thread.sleep, it suspends the current task without blocking a thread, and it respects cancellation:

Task {
    try await Task.sleep(nanoseconds: 2_000_000_000) // 2 seconds
    // This code runs after the delay
}

On iOS 16+, you can use the Duration-based overload Task.sleep(for:) for readability:

Task {
    try await Task.sleep(for: .seconds(2))
}

Task { ... } starts a new top-level task. It inherits the surrounding actor context only when created from an actor-isolated context (for example, inside a @MainActor-isolated method); otherwise the task body runs on the cooperative thread pool. If you need to update UI after the delay, hop to the main actor explicitly with await MainActor.run { ... } (or annotate the surrounding code with @MainActor).