DispatchQueue.main VS RunLoop.main: Main difference |Combine

Scheduler:

Kailash Ninawe
3 min readFeb 14, 2022

The Combine framework provides the Scheduler protocol, which is a powerful abstraction for describing how and when units of work are executed. We can use a scheduler to execute code as soon as possible, or after a future date. It provides many disparate ways of executing work, such as DispatchQueue, RunLoop and OperationQueue.

Let’s consider the example below,

private var subscriptions = Set<AnyCancellable>()
func fetchMovies(from endpoint: Endpoint) -> Future<[Movie], MovieStoreAPIError> {
return Future<[Movie], MovieStoreAPIError> {[unowned self] promise
//...
// Schedule to receive the sink closure on the
// main dispatch queue.
.receive(on:
DispatchQueue.main)
.sink(receiveCompletion: { (completion) in
// ...
}, receiveValue: { promise(.success($0.results)) })
.store(in: &self.subscriptions)
}
}

As we can see in the above code example, we’re making sure to receive the movies on the main thread using the DispatchQueue.main scheduler. The main queue scheduler is required since we need to perform UI updates on the main thread.

What is DispatchQueue.main?

It is the dispatch queue associated with the main thread of the current process. The system automatically creates the main queue and associates it with your application’s main thread. Tasks are being executed serially or concurrently by dispatch queue on its associated thread.

What is RunLoop.main?

A RunLoop object processes input for sources, such as mouse and keyboard events from the window system and Port objects or touch events from an application. A RunLoop object also processes Timer events.

Your application neither creates nor explicitly manages RunLoop objects. The system creates a RunLoop object as needed for each Thread object. The system is also responsible for creating the main run loop representing the main thread.

RunLoop.main and DispatchQueue.main are the similar but different:

From above definition's ,both RunLoop.main and DispatchQueue.main execute their code on the main thread, meaning that you can use both for updating the user interface. Both schedulers allow us to update the UI after a Combine value was published, which is why there’s no apparent reason stopping us from using one or another. Though, there are some essential differences to know.

The differences between RunLoop.main and DispatchQueue.main:

The main difference between RunLoop.main and DispatchQueue.main is that execution of code. DispatchQueue.main execute code directly where as RunLoop may be busy. RunLoop.main scheduler is executed using perform selector while in case of DispatchQueue.main is executed using gcd method.

RunLoop.main will give callback only when it is working in default mode and it is not the case always when user is interacting with the device or performing some actions. for example, suppose you are downloading image from the API while scrolling the table view using RunLoop.main as a scheduler then your image wont be shown on your view screen until you stop interacting with the screen. Because you have used RunLoop as a scheduler and it is busy in handling user interaction, So in this case we should always use DispatchQueue.main as a scheduler.

When should I use RunLoop.main as a scheduler:

Updating the UI while scrolling might affect the performance and smooth scrolling. In that case where your UI update isn’t as necessary when the user is scrolling, then it might make sense to use RunLoop.main as a scheduler.

Useful Links 👇🏼

https://github.com/apple/swift/blob/b... https://github.com/apple/swift/blob/2...

--

--

Kailash Ninawe

I am senior iOS developer. Just trying to help iOS community and learn something new as well.