Concurrency and Multithreading in Swift: A Complete Guide

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 to Concurrency and Multithreading in Swift



Modern iOS and macOS applications must be fast and responsive. The user should not have to wait while an image loads or a complex calculation completes. For this purpose, Swift provides concurrency and multithreading mechanisms. In this article, we will explore key concepts, starting from the classic Grand Central Dispatch (GCD) to the modern async/await from Swift Concurrency.



Concurrency is the ability of a program to execute multiple tasks simultaneously by switching between them. Multithreading is one implementation of concurrency, where tasks are executed on different operating system threads. Swift provides powerful and safe tools for working with these concepts.



1. Grand Central Dispatch (GCD) — The Foundation of Multithreading



Grand Central Dispatch (GCD) is a low-level Apple library for managing parallel code execution. It operates based on dispatch queues.



Types of Queues in GCD

  • Serial Queue — tasks are executed one after another in the order they are added.
  • Concurrent Queue — tasks can be executed simultaneously on different threads.
  • Main Queue — executes tasks on the main thread, intended for updating the UI.
  • Global Queues — system concurrent queues with different priorities (QoS).


Example: Performing a Task in the Background and Updating the UI



import UIKit

class ViewController: UIViewController { @IBOutlet weak var imageView: UIImageView!

func loadImage(from url: URL) { // Switch to a background queue DispatchQueue.global(qos: .background).async { [weak self] in guard let data = try? Data(contentsOf: url), let image = UIImage(data: data) else { return } // Return to the main thread to update the UI DispatchQueue.main.async { self?.imageView.image = image } } }}


In this example, we download the image on a background thread to avoid blocking the interface, and then update the UI on the main thread.



DispatchGroup for Task Synchronization



DispatchGroup allows you to track the execution of a group of asynchronous tasks and perform an action after they all complete.



let group = DispatchGroup()

group.enter()fetchData1 { result in print("Data 1 received") group.leave()}

group.enter()fetchData2 { result in print("Data 2 received") group.leave()}

group.notify(queue: .main) { print("All data loaded, updating UI")}


2. OperationQueue — An Object-Oriented Approach



OperationQueue is a higher-level abstraction built on top of GCD. It works with Operation objects, which represent a unit of work.



Advantages of OperationQueue

  • Ability to cancel tasks (cancel).
  • Setting dependencies between operations (addDependency).
  • Monitoring operation state (isFinished, isExecuting, isCancelled).
  • Configuring the maximum number of concurrent operations.


Example: Loading Multiple Images



import UIKit

class ImageLoader { private let queue = OperationQueue() func loadImages(from urls: [URL], completion: @escaping ([UIImage]) -> Void) { var images: [UIImage] = [] let completionOperation = BlockOperation { DispatchQueue.main.async { completion(images) } } for url in urls { let o

Blogs

Book Recommendations