Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Core Programming Concepts  ›  Capstone Project

Concurrency: GCD and async/await

Concurrency (GCD, async/await) 12 minSlow work must run off the main thread using GCD queues or async/await, and every UI update must return to the main thread.

Concurrency: GCD and async/await

The hiker taps "Sync" and TrailLog freezes for four seconds while it uploads. Scrolling stops, buttons ignore taps. The upload itself is fine — it simply ran on the wrong thread.

The rule

Slow work off the main thread; UI updates back on it.

iOS runs your UI on the main thread. It handles touches, layout and drawing, roughly 60 to 120 times per second. If you block it with slow work — network, big files, heavy maths — the app freezes. And UIKit may only be touched from the main thread.

Grand Central Dispatch (GCD) is the older tool. You send closures to queues: the main queue, or global background queues.

DispatchQueue.global().async {
    let data = self.buildBackup()          // slow, off the main thread
    DispatchQueue.main.async {
        self.statusLabel.text = "Synced"   // UI, back on the main thread
    }
}

async/await (Swift 5.5) is the modern tool. An async function can pause while it waits without blocking a thread; callers write await at each possible pause:

func uploadJournal() async throws -> Int {
    let (data, _) = try await URLSession.shared.data(for: request)
    return data.count
}

@MainActor
func syncTapped() {
    Task {
        statusLabel.text = "Syncing…"
        let bytes = try await uploadJournal()   // waits without freezing
        statusLabel.text = "Synced \(bytes) bytes"
    }
}
  • Task { } starts async work from ordinary, non-async code such as a button handler.
  • @MainActor guarantees code runs on the main thread, so UI updates are safe. View controllers in UIKit are main-actor types.
  • The code reads top to bottom, like normal code, instead of nesting closures inside closures.

The shortcut

For each line, ask: "Could this take noticeable time?" If yes, it goes off the main thread (await an async API or dispatch it). Then ask: "Does this touch the UI?" If yes, it must run on the main thread.

TrailLog's Sync buttonStep 1 of 4
  1. Button tapped

    Runs on the main thread. Start a Task so async work can begin.

The trap in GCD code: updating a label inside DispatchQueue.global().async without hopping back to DispatchQueue.main. It may seem to work, then glitch or crash at random.

Drills

Answer fast — trust the rule.

quick drill

Why does a slow network call on the main thread freeze the app?

quick drill

In GCD, where must label.text = "Done" be dispatched?

quick drill

What starts async work from a normal, non-async button handler?

quick drill

What does @MainActor guarantee?

Remember

  • Slow work off the main thread; UI updates back on it.
  • GCD: DispatchQueue.global().async, then DispatchQueue.main.async for UI. Modern: async/await, Task, @MainActor.
  • Trap: touching UIKit from a background queue works until it randomly does not.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainLoad weather without freezing

TrailLog's trail screen fetches a weather forecast from the network, then shows it in a label. Describe how to do this with async/await so the UI never freezes, and say which lines run on the main thread and why.