← All courses

Networking & Concurrency

🗓 May 31, 2026 ⏱ 2 min read

The main thread must stay free

iOS draws the UI and handles touches on the main thread. If you run a slow task there — like a network request — the whole app freezes. So networking must happen on a background thread, and only the final UI update returns to the main thread. Modern Swift makes this clean with async/await.

Decoding JSON into types

APIs return JSON. Swift turns JSON into your types automatically if they conform to Decodable:

struct User: Decodable {
    let id: Int
    let name: String
    let email: String
}

Fetching with async/await

func fetchUsers() async throws -> [User] {
    let url = URL(string: "https://api.example.com/users")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode([User].self, from: data)
}

async marks a function that can pause; await waits for the result without blocking the thread; throws lets it report errors. This reads top-to-bottom like normal code, but runs efficiently in the background.

Calling it and updating the UI

Task {
    do {
        let users = try await fetchUsers()
        await MainActor.run {
            self.users = users
            self.tableView.reloadData()    // UI work on the main thread
        }
    } catch {
        print("Failed: \(error)")
    }
}

@MainActor / MainActor.run guarantees UI code runs on the main thread — updating the UI from a background thread is a classic crash.

A quick word on GCD (the older way)

Before async/await, iOS used Grand Central Dispatch (GCD) with dispatch queues. You’ll still see it in existing code:

DispatchQueue.global().async {
    // background work
    DispatchQueue.main.async {
        // back on the main thread for UI
    }
}

For new code, prefer async/await — it’s safer and far more readable.

Handling errors and states

Networks fail: no internet, server errors, bad data. Always handle errors and reflect them in the UI (a message, a retry button) rather than letting the app appear frozen or crash.

Common mistakes

  • Updating the UI from a background thread (use the main thread / @MainActor).
  • Ignoring errors — always catch and inform the user.
  • Forgetting the App Transport Security rules (iOS requires HTTPS by default).
Summary: Use URLSession + async/await + Decodable to fetch and parse data off the main thread, then hop back to the main thread (@MainActor) for UI updates. Always handle errors gracefully.