Sobes.tech
Middle

Why is it not recommended to load the main thread in iOS, and what are the ways to avoid this?

sobes.tech AI

Answer from AI

The main thread is responsible for updating the user interface and handling UI events. Any long-running operations (network requests, heavy computations, file operations) on the main thread will block it, leading to a "frozen" UI, low app responsiveness, and even crashes (watchdog timeout).

Ways to avoid load on the main thread:

  1. Grand Central Dispatch (GCD): Allows executing tasks asynchronously on background queues.

    • DispatchQueue.global(): Accessing global background queues with different priorities.
    • DispatchQueue.main: Main queue for UI updates.
    • async { ... }: Executing a block of code asynchronously.
    // executing heavy task on background thread
    DispatchQueue.global(qos: .userInitiated).async {
        // heavy computations or network request
    
        // updating UI on main thread
        DispatchQueue.main.async {
            // update user interface
        }
    }
    
  2. Operation Queues (Operation & OperationQueue): A higher-level abstraction over Dispatch Queues, supporting dependencies between operations, cancellation, and suspension.

    • OperationQueue(): Creating an operation queue.
    • Operation: Abstract class for tasks that can be added to the queue.
    • addOperation(_:): Adding an operation to the queue.
    let downloadOperation = BlockOperation {
        // download data
    }
    
    let parseOperation = BlockOperation {
        // parse data
    }
    
    // dependency: parsing starts after download
    parseOperation.addDependency(downloadOperation)
    
    let queue = OperationQueue()
    queue.addOperation(downloadOperation)
    queue.addOperation(parseOperation)
    
  3. Concurrency (async/await): Modern approach in Swift for asynchronous programming.

    • async: Marks a function as asynchronous.
    • await: Waits for an asynchronous operation to complete.
    • Task: Creating a new concurrent task.
    func fetchData() async throws -> Data {
        let url = URL(string: "https://example.com/data")!
        let (data, _) = try await URLSession.shared.data(from: url)
        return data
    }
    
    func updateUI() {
        Task {
            do {
                let data = try await fetchData()
                // process data
                // update UI on main thread automatically
                // (if Task is created in the main Actor or DispatchQueue.main)
            } catch {
                // handle error
            }
        }
    }
    
  4. Actors: Prevent data races when accessed from different concurrent contexts, providing exclusive access to their state.

    actor DataStore {
        private var data: [String] = []
    
        func addData(_ item: String) {
            data.append(item)
        }
    
        func getData() -> [String] {
            return data
        }
    }
    
    let store = DataStore()
    
    Task {
        await store.addData("item1")
    }
    

Choosing between GCD, Operation Queues, and Concurrency depends on the complexity of the task, the level of control needed, and code structuring requirements. Concurrency is the preferred approach in modern applications due to its readability and safety.

Why is it not recommended to load the main thread in… - sobes.tech