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 increasingly require executing multiple tasks simultaneously. Whether it's loading data from the network, processing images, or complex mathematical calculations — proper thread management is essential. In Swift, concurrency and multithreading are implemented at the highest level, providing developers with powerful and safe tools.

In this article, we will cover three main approaches: Grand Central Dispatch (GCD), OperationQueue, and the modern async/await keyword, introduced in Swift 5.5. You will learn how to choose between them correctly and avoid common mistakes such as race conditions and deadlocks.



Grand Central Dispatch (GCD) — Classic Multithreading in Swift

Grand Central Dispatch (GCD) is a low-level Apple library that manages a thread pool. The developer does not need to create and destroy threads manually — GCD does this automatically, distributing tasks across available CPU cores.



Main GCD Queues

In GCD, there are two types of queues: serial and concurrent. Tasks in a serial queue are executed one after another, while in a concurrent queue they can be launched simultaneously.

// Creating a serial queuelet serialQueue = DispatchQueue(label: "com.example.serial")serialQueue.async {    print("Task 1")}serialQueue.async {    print("Task 2")}

// Creating a concurrent queuelet concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)concurrentQueue.async { print("Concurrent task A")}concurrentQueue.async { print("Concurrent task B")}


Global Queues and QoS

GCD provides ready-made global queues with different priorities (Quality of Service). Use them when you don't need to create your own queue:

  • .userInteractive — for UI updates (highest priority)
  • .userInitiated — for tasks initiated by the user
  • .utility — for long-running background operations
  • .background — for tasks that are not time-critical
DispatchQueue.global(qos: .userInitiated).async {    // Heavy work    let result = performComplexCalculation()    DispatchQueue.main.async {        // Return to the main thread for UI updates        self.updateUI(with: result)    }}

Important: Always update the UI only on the main thread (DispatchQueue.main). Ignoring this rule leads to unpredictable application crashes.



OperationQueue — Managing Task Dependencies

If GCD is a "Swiss Army knife", then OperationQueue is a full-fledged scheduler with the ability to cancel, prioritize, and create dependencies between operations. Each operation is an instance of the Operation class (or its subclass BlockOperation).



Creating and Executing Operations

let operationQueue = OperationQueue()operationQueue.maxConcurrentOperationCount = 2 // Limiting the number of concurrent operations

let operation1 = BlockOperation { print("Operation 1")}let operation2 = BlockOperation { print("Operation 2")}

// Adding a dependency: operation2 will start only after operation1 completesoperation2.addDependency(operation1)

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


Cancellation and Priorities

Unlike GCD, OperationQueue allows you to cancel a specific operation or the entire queue:

// Cancelling one operationoperation1.cancel()

// Cancelling the entire queueoperationQueue.cancelAllOperations()

// Setting priorityoperation1.

Blogs

Book Recommendations