Working with Task Queues in Swift: GCD and OperationQueue

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Do You Need Task Queues in Swift?

In modern iOS and macOS development, application performance directly depends on how efficiently you manage threads. Long-running operations — loading data from the network, processing images, working with a database — should not block the main thread (Main Thread), which is responsible for the interface. If the main thread is busy, the user sees a "frozen" screen, and the system may forcefully terminate the application.

Task Queues solve this problem. They allow you to execute code asynchronously, in parallel or sequentially, without blocking the UI. In Swift, there are three main tools for this: Grand Central Dispatch (GCD), OperationQueue, and the modern async/await model (Swift Concurrency). In this article, we will break them down with practical examples and figure out when to use what.



1. Grand Central Dispatch (GCD): The Classic Approach

GCD is a low-level C library that manages a pool of threads. In Swift, it is convenient to work with it through global queues (DispatchQueue).



1.1. Main Types of Queues in GCD

  • Main Queue — a serial queue on the main thread. Used for updating the UI.
  • Global Queues — concurrent queues with different priorities (QoS: .userInteractive, .userInitiated, .utility, .background).
  • Custom Queues — queues you create yourself (can be serial or concurrent).


1.2. Example: Loading Data in the Background and Updating the UI

Suppose we need to load JSON from a remote server and display the result in a table.

DispatchQueue.global(qos: .userInitiated).async {    // 1. Background work: loading data    guard let url = URL(string: "https://api.example.com/data") else { return }    let data = try? Data(contentsOf: url)        // 2. Return to the main thread to update the UI    DispatchQueue.main.async {        // Update the data model or table        self.tableView.reloadData()    }}


1.3. Using DispatchGroup for Synchronization

When you need to perform several parallel tasks and wait for them all to complete, use DispatchGroup.

let group = DispatchGroup()var images: [UIImage] = []

for url in imageURLs { group.enter() DispatchQueue.global().async { if let data = try? Data(contentsOf: url), let image = UIImage(data: data) { images.append(image) } group.leave() }}

group.notify(queue: .main) { print("All images loaded: \\(images.count)") // Update UI}


2. OperationQueue: A Higher Level of Abstraction

OperationQueue is built on top of GCD but provides additional capabilities: cancelling tasks, dependencies between operations, and controlling the maximum number of concurrent operations.



2.1. Creating and Running Operations

Operations can be created using BlockOperation or by subclassing Operation.

let queue = OperationQueue()queue.maxConcurrentOperationCount = 2 // Limit to 2 parallel tasks

let operation1 = BlockOperation { print("Operation 1 started") sleep(2) print("Operation 1 finished")}

let operation2 = BlockOperation { print("Operation 2 started") sleep(1) print("Operation 2 finished")}

// Set dependency: operation2 will only start after operation1operation2.addDependency(operation1)

queue.addOperations([operation1, operation2], waitUntilFinished: false)


2.2. Cancelling Operations

Unlike GCD, operations can be cancelled. This is critical if the user navigates away from the screen, while

Blogs

Book Recommendations