Concurrency and Threading, Asynchronous Programming

Overview

What is concurrency?
Apple A-series chips

Threads

Overview
Analogy

Concurrency and Threads in Apple Ecosystem

Overview
Queues
1
2
3
DispatchQueue.main.async {
    completionHandler(pokemonData)
}
Grand Central Dispatch
Example of GCD in Playground
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import PlaygroundSupport
import UIKit

// Necessary to keep the playground running
PlaygroundPage.current.needsIndefiniteExecution = true

// Simulate a heavy task on a background queue
DispatchQueue.global(qos: .background).async {
    print("🚀 Heavy task started on background thread.")

    // Simulate heavy computation or network task
    for i in 1...5 {
        print("Processing item \(i)")
        sleep(1)  // Simulate a time-consuming task
    }

    print("✅ Heavy task completed!")

    // Update UI or execute main-thread task after background work
    DispatchQueue.main.async {
        print("🎉 Updating UI on main thread.")
        // e.g., update labels, images, etc.
    }
}

print("⏳ Main thread continues executing without waiting...")
Drawback of GCD: callback hell
Example
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

func fetchUser(completion: @escaping (String) -> Void) {
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        completion("User123")
    }
}

func fetchOrders(for user: String, completion: @escaping ([String]) -> Void) {
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        completion(["Order1", "Order2", "Order3"])
    }
}

func fetchOrderDetails(order: String, completion: @escaping (String) -> Void) {
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        completion("Details for \(order)")
    }
}

// Callback Hell Example
fetchUser { user in
    print("User fetched: \(user)")

    fetchOrders(for: user) { orders in
        print("Orders fetched: \(orders)")

        fetchOrderDetails(order: orders[0]) { details in
            print("First order details fetched: \(details)")

            fetchOrderDetails(order: orders[1]) { details in
                print("Second order details fetched: \(details)")

                fetchOrderDetails(order: orders[2]) { details in
                    print("Third order details fetched: \(details)")

                    DispatchQueue.main.async {
                        print("✅ Finally done updating UI.")
                    }
                }
            }
        }
    }
}

Solution for callback hell: async/await
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

// Async functions using Swift concurrency
func fetchUser() async -> String {
    try? await Task.sleep(nanoseconds: 1_000_000_000)
    return "User123"
}

func fetchOrders(for user: String) async -> [String] {
    try? await Task.sleep(nanoseconds: 1_000_000_000)
    return ["Order1", "Order2", "Order3"]
}

func fetchOrderDetails(order: String) async -> String {
    try? await Task.sleep(nanoseconds: 1_000_000_000)
    return "Details for \(order)"
}

Task {
    let user = await fetchUser()
    print("User fetched: \(user)")

    let orders = await fetchOrders(for: user)
    print("Orders fetched: \(orders)")

    for order in orders {
        let details = await fetchOrderDetails(order: order)
        print("Order details fetched: \(details)")
    }

    DispatchQueue.main.async {
        print("✅ Finally done updating UI.")
    }
}