Operation Queues in Swift

Hasaan AliHasaan Ali
3 min read

In Swift, operation queues let you perform background processing work easily. Simply as:

import Foundation

// Create an operation queue
let operationQueue = OperationQueue()

// Pass it a block of code
operationQueue.addOperation {
    print("Operation 1")
}
// Or, pass it a BlockOperation object
let blockOperation = BlockOperation {
    print("A block operation")
}
operationQueue.addOperation(blockOperation)

// and there are other ways too.

Output:
Operation 1
A block operation

When an operation is added to an operation queue, it is ready to run by the operation queue.

Concurrent (by default)

By default, an operation queue runs the added operations concurrently. In the below example, we add 100 operations to an operation queue and they start running concurrently as soon as we add them:

import Foundation

// Create an operation queue
let operationQueue = OperationQueue()

// Start 100 operations
for i in 0..<100 {
    operationQueue.addOperation {
        print("operation \(i)")
    }
}

Output:
operation 7
operation 4
operation 0
operation 8
operation 6
...
operation 99

Serial Operation Queue? Almost.

Sometimes you may want to run the operations, one by one, in the exact order you added them to the queue.

By setting the maxConcurrentOperationCount = 1 on the operation queue, we can tell it to run just one operation at a time, and because we're adding the operations in order, the operation queue picks and runs them in the same order, one by one.

let operationQueue = OperationQueue()

operationQueue.maxConcurrentOperationCount = 1

for i in 0..<100 {
    operationQueue.addOperation {
        print("operation \(i)")
    }
}

Output:
operation 0
operation 1
operation 2
operation 3
operation 4
...
operation 99

Beware, not so serial.

The next operation that gets picked to run depends on its queuePriority.

In the above last example, our operations ran sequentially because all of them had the default .normal queue priority. The operation.queuePriority has five possible values: .veryHigh, .high, .normal, .low, & .veryLow .

In the below example, we add 20 operations to an operation queue such that each 4th operation has a high queue priority. This time let's add a one-second sleep inside our block operations to mimic some time-taking tasks:

let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
for i in 0..<20 {
    let op = BlockOperation {
        print("operation \(i)")
        sleep(1)
    }
    // Every 4th operation has a high queue priority
    if i%4 == 0 {
        op.queuePriority = .high
    }
    operationQueue.addOperation(op)
}

Output:
operation 0
operation 4
operation 8
operation 12
operation 16
operation 1
operation 2
operation 3
operation 5
operation 6
operation 7
operation 9
operation 10
operation 11
operation 13
operation 14
operation 15
operation 17
operation 18
operation 19

In the above output, you can see how the queue priority of the operations changed their running order.

Conclusion

This conveys the true meaning of the operation queue's maxConcurrentOperationCount property. By setting it to 1, it only guarantees that at one time, there will be just one running operation. It doesn't guarantee that the running order of the operations would be the same as the order they get added to the queue.

Do you know of any other factors that affect the running order of the operations added to an operation queue? Share your thoughts in the comments.

0
Subscribe to my newsletter

Read articles from Hasaan Ali directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hasaan Ali
Hasaan Ali