Operation and OperationQueue Tutorial in Swift

In this tutorial, you will create an app that uses concurrent operations to provide a responsive interface for users by using Operation and OperationQueue. By James Goodwill.

4.6 (78) · 3 Reviews

Download materials
Save for later
Share
Update note: James Goodwill updated this tutorial for Xcode 10 and Swift 4.2. Soheil Azarpour wrote the original post and Richard Turton completed a previous update.

Learn how to use Operations in your app!

OperationQueue

Everyone has had the frustrating experience of tapping a button or entering some text in an iOS or Mac app, when all of a sudden: WHAM! The user interface stops responding.

On the Mac, your users get to stare at the colorful wheel rotating for a while until they can interact with the UI again. In an iOS app, users expect apps to respond immediately to their touches. Unresponsive apps feel clunky and slow, and usually receive bad reviews.

Keeping your app responsive is easier said than done. Once your app needs to perform more than a handful of tasks, things get complicated quickly. There isn’t much time to perform heavy work in the main run loop and still provide a responsive UI.

What’s a poor developer to do? The solution is to move work off the main thread via concurrency. Concurrency means that your application executes multiple streams (or threads) of operations all at the same time. This way the user interface stays responsive as you’re performing your work.

One way to perform operations concurrently in iOS is with the Operation and OperationQueue classes. In this tutorial, you’ll learn how to use them! You’ll start with an app that doesn’t use concurrency at all, so it will appear very sluggish and unresponsive. Then, you’ll rework the app to add concurrent operations and provide a more responsive interface to the user!

Getting Started

The overall goal of the sample project for this tutorial is to show a table view of filtered images. The images are downloaded from the Internet, have a filter applied, and then displayed in the table view.

Here’s a schematic view of the app model:

Preliminary Model

OperationQueue

A First Try

Use the Download Materials button at the top or bottom of this tutorial to download the starter project. It is the first version of the project that you’ll be working on in this tutorial.

Note: All images are from stock.xchng. Some images in the data source are intentionally mis-named, so that there are instances where an image fails to download to exercise the failure case.

Build and run the project, and (eventually) you’ll see the app running with a list of photos. Try scrolling the list. Painful, isn’t it?

Classic photos, running slowly

Classic photos, running slowly

All of the action is taking place in ListViewController.swift, and most of that is inside tableView(_:cellForRowAtIndexPath:).

Have a look at that method and note there are two things taking place that are quite intensive:

  1. Loading the image data from the web. Even if this is easy work, the app still has to wait for the download to be complete before it can continue.
  2. Filtering the image using Core Image. This method applies a sepia filter to the image. If you would like to know more about Core Image filters, check out Beginning Core Image in Swift.

In addition, you’re also loading the list of photos from the web when it is first requested:

lazy var photos = NSDictionary(contentsOf:dataSourceURL)!

All of this work is taking place on the main thread of the application. Since the main thread is also responsible for user interaction, keeping it busy with loading things from the web and filtering images is killing the responsiveness of the app. You can get a quick overview of this by using Xcode’s gauges view. You can get to the gauges view by showing the Debug navigator (Command-7) and then selecting CPU while the app is running.

Xcode’s Gauges view, showing heavy lifting on the main thread

Xcode's Gauges view, showing heavy lifting on the main thread

You can see all those spikes in Thread 1, which is the main thread of the app. For more detailed information, you can run the app in Instruments, but that’s a whole other tutorial. :]

It’s time to think about how can you improve that user experience!

Tasks, Threads and Processes

Before going further, there are a few technical concepts you need to understand. Here are some key terms:

  • Task: a simple, single piece of work that needs to be done.
  • Thread: a mechanism provided by the operating system that allows multiple sets of instructions to operate at the same time within a single application.
  • Process: an executable chunk of code, which can be made up of multiple threads.

The Foundation framework contains a class called Thread, which is much easier to deal with, but managing multiple threads with Thread is still a headache. Operation and OperationQueue are higher level classes that have greatly simplified the process of dealing with multiple threads.

Note: In iOS and macOS, the threading functionality is provided by the POSIX Threads API (or pthreads) and is part of the operating system. This is pretty low level stuff, and you’ll find that it’s easy to make mistakes; perhaps the worst thing about threads is those mistakes can be incredibly hard to find!

In this diagram, you can see the relationship between a process, threads, and tasks:

Process, Thread and Task

OperationQueue

As you can see, a process can contain multiple threads of execution, and each thread can perform multiple tasks one at a time.

In this diagram, thread 2 performs the work of reading a file, while thread 1 performs user-interface related code. This is quite similar to how you should structure your code in iOS — the main thread performs any work related to the user interface, and secondary threads perform slow or long-running operations such as reading files, accessing the network, etc.

Operation vs. Grand Central Dispatch (GCD)

You may have heard of Grand Central Dispatch (GCD). In a nutshell, GCD consists of language features, runtime libraries, and system enhancements to provide systemic and comprehensive improvements to support concurrency on multi-core hardware in iOS and macOS. If you’d like to learn more about GCD, you can read our Grand Central Dispatch Tutorial.

Operation and OperationQueue are built on top of GCD. As a very general rule, Apple recommends using the highest-level abstraction, then dropping down to lower levels when measurements show this is necessary.

Here’s a quick comparison of the two that will help you decide when and where to use GCD or Operation:

  • GCD is a lightweight way to represent units of work that are going to be executed concurrently. You don’t schedule these units of work; the system takes care of scheduling for you. Adding dependency among blocks can be a headache. Canceling or suspending a block creates extra work for you as a developer!
  • Operation adds a little extra overhead compared to GCD, but you can add dependency among various operations and re-use, cancel or suspend them.

This tutorial will use Operation because you’re dealing with a table view and, for performance and power consumption reasons, you need the ability to cancel an operation for a specific image if the user has scrolled that image off the screen. Even if the operations are on a background thread, if there are dozens of them waiting on the queue, performance will still suffer.

James Goodwill

Contributors

James Goodwill

Author

Felicity Johnson

Tech Editor

Michael Briscoe

Final Pass Editor

Richard Critz

Team Lead

Over 300 content creators. Join our team.