dispatch_async versus Task.detached for offloading work
GCD versus structured concurrency.
dispatch_async submits a closure to a queue; Task.detached starts an unstructured async task off the current actor without inheriting context.
WHAT THIS TESTS It checks whether you understand both concurrency models and resist overusing Task.detached, which discards the context that structured concurrency exists to preserve.
GCD: DISPATCH_ASYNC dispatch_async submits a closure to a serial or concurrent dispatch queue backed by a thread pool. There is no await, no cancellation propagation, and no structured parent-child relationship; you manage callbacks and synchronization yourself. It remains useful for low-level scheduling, integrating with callback-based APIs, and fine-grained queue control.
SWIFT CONCURRENCY: TASK.DETACHED Task.detached spawns an unstructured task that explicitly does not inherit the current actor, priority, or task-local values, so its body runs off the main actor by default. Because it is detached it is not part of the surrounding task tree, so cancellation and structured cleanup do not flow automatically. A plain Task, by contrast, inherits context and is usually what you want.
WHICH TO CHOOSE For offloading CPU work in an async codebase, prefer calling an async function or putting the work behind an actor or a nonisolated function, letting the runtime hop executors. Use Task.detached only when you genuinely must break actor inheritance, for example to avoid hopping back to the main actor for purely background work. Use dispatch_async when interoperating with existing GCD code or needing explicit queue semantics.
COMMON WRONG ANSWERS Treating Task.detached as the standard way to leave the main thread, which loses priority and task-local propagation and invites data races. Believing dispatch_async participates in Swift cancellation.
LIKELY FOLLOW-UPS What does a non-detached Task inherit? How does actor isolation move work off the main thread without detaching? How does cancellation differ between the two? What are task-local values?
ONE CONCRETE EXAMPLE Inside a @MainActor view model you need to parse a large file. Writing Task.detached { parse() } leaves the main actor but drops priority and cancellation. A cleaner approach is an actor or nonisolated async function you await, which offloads the work while keeping the task tree, cancellation, and priority intact.
Read the original → docs.swift.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.