A guide to Concurrency in iOS

Nov 10, 2022

When it comes to an app, performance is at the cutting edge, and no iPhone user likes to wait impatiently for a response after acting. In the event that it does, the app will receive a poor review.

This blog will talk about the specific approach to performance inflation known as Concurrency.

Concurrency is the potentiality of numerous elements, program units, algorithms, or problems to be executed on the verge of influencing the ultimate outcome. The parallel implementation of the concurrent units can noticeably enhance the overall pace of execution in systems with multiple processors and cores. In iOS development, concurrency is used to improve productivity and UI responsiveness and is provided by several tools like Thread, GCD (Grand Central Dispatch), and Operation.

In iOS, code can be launched concurrently primarily in two ways:

1.    Grand Central Dispatch (GCD)

2.    Operations and Operation Queues

Operations are stacked on top of the GCD; hence, it is adaptable and has high abstraction. However, occasionally, scooping into GCD is mandatory to avail the benefits in contemplation of acquiring a systematic working method of concurrency.

Grand Central Dispatch:

GCD is a technology for Apple’s Mac OS X and iOS operating systems that is a combination of extensions to the C language, an API, and a run-time library that allows application developers to identify sections of code to execute in parallel. Grand Central Dispatch (GCD) can more effectively meet the demands of running programs by balancing their needs with the available system resources.

1.    Queues: Queues are data structures that manage objects in the order First-In-First-Out (FIFO).

2.    Dispatch Queues: These queues take the code or ingot of codes and implement those ingots on separate threads asynchronously and concurrently. It impulsively creates and reiterates a puddle of threads. Whenever a pre-existing thread prevails, it can be rephrased, and if not, OS will create the thread accordingly.

There are two sorts of dispatch queues:

  • Serial queues are main dispatch queues that are pre-defined, have a single thread associated with them, and thus execute only one task at a time. Multiple serial queues can be exploited for parallel runs. This allows for consecutive access to the split resources, breaking out race conditions, and tasks executing in a predictable order.
  • Concurrent Queues are global, pre-defined queues that can utilize enormous threads as the system has resources for the execution of multiple tasks in parallel, simultaneously. A thread will be developed and launched as required in the concurrent queue. In the concurrent queues, the system pulls a closure at the top of the queue and initiates its execution in a certain thread.

By default, the system provides a single serial queue and six concurrent queues, which are differentiated only by the priority level. The main dispatch queue is the universal serial queue, administering all the tasks on the main thread.

Synchronous and asynchronous tasks:

When a task is functioning synchronously, the current queue will be hindered prior to the task's fulfillment before moving ahead to another task. Any synchronous task cannot be carried out until it is related to the UI.

Asynchronous tasks in the current queue will begin but return control to the current queue immediately. This way, the current queue will be disentangled, and one can perform other tasks on the current queue.

Quality of service (QoS):

It helps to categorize the tasks in the dispatch queue by priority. It specifies a limit for the number of concurrent appeals allowed to the API. Higher-priority tasks perform faster than lower-priority tasks.

Distinct QoS include:

  • .userInteractive: This is endorsed for tasks that the end user precisely interacts with. It is substantially linked to UI, namely animations, event handling, and updating the app’s user interface. If stagnant tasks occur, then it may freeze the screen.
  • .userInitiated: One can perform the task that starts after user interaction, such as document opening, fetching data, etc.; these tasks can be performed and reflected in the UI.
  • .utility: The tasks that are not tracked actively by the user are accomplished. It also has a progress indicator for long-running computations or downloading.
  • .background: Database maintenance and backup can be performed.
  • .default and .unspecified: These are infrequently used with QoS.


After using GCD, one can submit tasks concurrently, but if one demands supplementary customization for tasks, then operations came into existence.

Subsequently, when creating operations, one requisite is to add them to the operation queue by instantiating a queue object with the Operation Queue class. Operations start after adjoining them into the queue.

Contrary to GCD, operations and function queues are uncorrelated to FIFO. In operation queues, one can coordinate the priority of execution of operations; hence, those with the highest priority will be executed earliest. Dependencies can also be added on, so operations belonging to others will not be executed until the dependent task terminates.

By default, operation queues are concurrent, but to serialize them, assign "maxConcurrentOperationCount" to 1.

In iOS, the following two operation classes exist:

  • Block Operation
  • Invocation Operation

These operations are handy when one wants to pass a single function or chunk of code to the operation queues.

Assets of operations:

  • Dependencies can be totalized.
  • In operations, one can summarize objects.
  • Priority levels can be laid out on queues and can be countermanded by specific proceedings that have passed into the queues.
  • Operations can be abandoned if desired.

Why do we need concurrency?

  • Better user interface: After finalizing vigorous work on the main thread, the app’s UI will freeze, causing unresponsiveness to the users. Users are vulnerable to interacting with the UI, hence catalyzing disappointment. The UI does not freeze on the main thread because it is performing all the tasks with the background threads, so the user can interact with the app's user interface.
  • Execution of insistent functions speedily: By executing high CPU-demanding tasks in the background, an app's UI is responsive and grants an effortless experience to the users.
  • Engage device hardware: All iOS gadgets are multi-core processors, which permits developers to execute collective tasks in parallel.

Creating and handling threads are complex as they are dependent on hardware and on device capabilities such as batteries, processors, etc. The system looks after all the things, and as a developer, one needs to realize how to employ them suitably.

Throughout the working time on concurrency, three problems arise:

  • Race conditions
  • Priority invention
  • Deadlocks

The APIs provided by the system are cautious about the first two, and the last one is done by using APIs properly.

  • Task: An elementary, solitary piece of work that needs to be done.
  • Process: A process is an executable chunk of code made up of multiple threads.
  • Thread: A concurrently running mechanism that executes multiple sets of instructions

It has a catalog of instructions and sits on the disc until the user runs it. The OS then loads that process into memory, starts an instruction pointer, and has the CPU sequentially execute its instructions until they end, terminating the process.

Enhance your company's experience with Talrn

Hire immediately available developers on talrn.com. Talrn helps companies hire & onboard resources instantly, at zero effective cost. Contact hello@talrn.com for more information.



Adventure capitalist, Corporate slave & Unpaid intern.