Introduction to Task Queues in Swift
\n\nMobile and desktop applications in Swift often perform several operations simultaneously: loading data from the network, processing images, animating the interface. Without proper management of these tasks, the application may "freeze" or lag. Task queues are a mechanism that allows you to organize code execution in the background without blocking the main thread (UI).
\n\nIn Swift, there are three main approaches to working with queues:
\n- \n
- Grand Central Dispatch (GCD) — a low-level API for managing queues. \n
- OperationQueue — a higher-level abstraction over GCD. \n
- async/await (Swift 5.5+) — modern asynchronous syntax. \n
In this article, we will break down each approach with practical examples that are understandable even for beginners.
\n\n1. Grand Central Dispatch (GCD) — The Foundation of Queues
\n\nGCD is an Apple library that manages a pool of threads. You simply create a queue and dispatch tasks to it. The system itself decides which thread to execute the code on.
\n\nTypes of Queues in GCD
\n- \n
- Main Queue — executes tasks on the main thread (UI updates only). \n
- Global Queues — background queues with different priorities (userInteractive, userInitiated, utility, background). \n
- Private Queues — your own queues, which can be configured as serial or concurrent. \n
Example: Loading Data in the Background
\nimport UIKit\n\nclass ViewController: UIViewController {\n \n @IBOutlet weak var imageView: UIImageView!\n \n override func viewDidLoad() {\n super.viewDidLoad()\n \n // Dispatch a task to a background queue\n DispatchQueue.global(qos: .userInitiated).async {\n let url = URL(string: "https://example.com/image.jpg")!\n if let data = try? Data(contentsOf: url),\n let image = UIImage(data: data) {\n \n // Return to the main thread to update the UI\n DispatchQueue.main.async {\n self.imageView.image = image\n }\n }\n }\n }\n}\n\nSerial vs Concurrent Queues
\nA serial queue executes tasks one after another. A concurrent queue can launch several tasks simultaneously.
\n\n// Serial queue\nlet serialQueue = DispatchQueue(label: "com.example.serial")\nserialQueue.async { print("Task 1") }\nserialQueue.async { print("Task 2") }\n// Output: Task 1, then Task 2\n\n// Concurrent queue\nlet concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)\nconcurrentQueue.async { print("Task A") }\nconcurrentQueue.async { print("Task B") }\n// Output: Task A or Task B (order is not guaranteed)\n\n2. OperationQueue — Managing Dependencies
\n\nOperationQueue is a more convenient wrapper over GCD that allows you to:
\n- \n
- Set dependencies between tasks. \n
- Cancel task execution. \n
- Manage the maximum number of concurrent operations. \n
Creating and Executing Operations
\nlet operationQueue = OperationQueue()\noperationQueue.maxConcurrentOperationCount = 2 // maximum 2 tasks simultaneously\n\nlet operation1 = BlockOperation {\n print("Operation 1 is executing")\n Thread.sleep(forTimeInterval: 1)\n}\n\nlet operation2 = BlockOperation {\n print("Operation 2 is executing")\n Thread.sleep(forTimeInterval: 1)\n}\n\n// Set a dependency: operation2 will start only after operation1\noperation2.addDependency(operation1)\n\noperationQueue.addOperations([operation1, operation2], waitUntilFinished: false)\n\n