Asynchronous Queues - codepath/ios_guides GitHub Wiki
Asynchronous Queues
For time-consuming tasks such as network calls and reading/writing to disk, it is often a good idea to move those tasks off the main thread. This keeps the main thread free to handle UI updates so the app stays responsive.
For new code, the recommended approach is Swift's structured concurrency — async / await and Task. The language features back-deploy to iOS 13 via Xcode 13.2+, and the URLSession.shared.data(from:) async API used below requires iOS 15+. The Grand Central Dispatch (GCD) APIs further down are still supported and useful when you need to interoperate with completion-handler code or target older iOS versions.
Modern: structured concurrency (async / await)
Task {
do {
let (data, _) = try await URLSession.shared.data(from: url)
let image = UIImage(data: data)
// Touch UIKit on the main actor.
await MainActor.run {
imageView.image = image
}
} catch {
print("Image load failed: \(error)")
}
}
Task { ... } inherits actor isolation from the surrounding context — when started from an @MainActor-isolated context (the typical view-controller case) the task body runs on the main actor, while Task.detached { ... } would not inherit. If the surrounding context is not main-actor-isolated, hop to the main actor with await MainActor.run { ... } (or call an @MainActor-isolated method) before touching UIKit.
GCD: execute code on a background queue
When you need GCD, pick an explicit quality-of-service (QoS) class so the system can prioritize the work appropriately. Omitting QoS falls back to .default, which is rarely the right choice for a specific piece of background work.
DispatchQueue.global(qos: .userInitiated).async {
// Code to be executed asynchronously in the background.
}
Common QoS choices for global(qos:):
.userInteractive— work that must complete in the next frame or two (animations, hit-testing follow-ups). Rarely used for long-running tasks..userInitiated— work the user is actively waiting for, such as loading a view they just opened..utility— longer-running work the user knows about but isn't actively waiting on (downloads, imports)..background— maintenance work the user doesn't need to see (prefetching, log uploads).
GCD: execute code back on the main thread
When a task on a background queue produces data you want to display, hop back to the main queue before touching UIKit.
DispatchQueue.main.async {
// Code to be executed on the main thread.
}