Concurrency
Actors, isolation, Sendable, structured concurrency, and the Swift 6 rules that trip people up.
- Swift 6, can @MainActor do async work? Yes. Actor isolation controls where synchronous code runs, not whether it can suspend, so a @MainActor function can be async and await freely.
- Task vs Task.detached, and what is actor reentrancy? Task inherits the caller's actor context while Task.detached does not, and actors are reentrant at await points, so state can change mid-suspension.
- What does @concurrent do? @concurrent forces a function onto the background executor, opting out of Swift 6.2's new default where nonisolated async inherits the caller's actor.
- What is nonisolated(nonsending)? nonisolated(nonsending) keeps an async function on its caller's actor instead of hopping to the global executor, avoiding extra Sendable requirements.
- What is the difference between sending and consuming? consuming governs local ownership and lifetime on a single thread, while sending proves a value has no other references so it can safely cross actors.
- How does Swift enforce (or not enforce) thread safety? Swift proves data-race freedom at compile time through actor isolation and Sendable checking, but it can't catch reentrancy bugs or @unchecked escapes.
- What are Swift concurrency isolation rules? Two invariants govern isolation: you can't touch actor-isolated state off that actor, and you can't move a value across domains unless it's Sendable.
- Why is Sendable important? Sendable is how the compiler proves a value can cross isolation boundaries safely, turning whole classes of data race into compile-time errors.
- How does ARC interact with Swift concurrency? ARC has always used atomic retain and release since objects could cross threads, but it only keeps memory valid — Sendable is what makes contents safe.
- When do you still use GCD over async/await? GCD still earns its place for legacy callback APIs, DispatchGroup's heterogeneous waiting, and precise QoS control, not to dodge Sendable errors.
- How do you handle long-running tasks? Foreground work gets a Task with cancellation checks and progress reporting, while backgrounding needs BGTaskScheduler or a background URLSession.
- How do you prevent deadlocks? Avoid blocking a thread while holding a lock, never mix semaphores with async/await, and keep lock ordering consistent across call sites.
- How do you manage Core Data concurrency? Every NSManagedObjectContext is confined to its own queue, so access goes through perform, and objects cross contexts only by NSManagedObjectID.
- What is priority inversion? Priority inversion happens when a high-priority task waits on a lock held by a low-priority one that medium-priority work keeps preempting.