Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Schedulers for network requests in RxSwift

I’ve been learning Rxswift and applying it on a project since start. I would like your help to fell more assured about a concept.

I understand the changes in the UI should be performed on the Mainscheduler, and you should explicitly use .observeOn(MainSchedule… in case you don’t use Drivers.

My doubt is: normally, should I explicitly switch to a background thread when performing network requests?.

I haven’t found many literature talking about exactly this, but I’ve read some projects code and most of them don’t, but a few do. Those eventually use Drivers or .observeOn(MainSchedule… to make the changes on the UI.

In https://www.thedroidsonroids.com/blog/rxswift-examples-4-multithreading, for instance, he says

So as you may guessed, in fact everything we did was done on a MainScheduler. Why? Because our chain starts from searchBar.rx_text and this one is guaranteed to be on MainScheduler. And because everything else is by default on current scheduler – well our UI thread may get overwhelmed. How to prevent that? Switch to the background thread before the request and before the mapping, so we will just update UI on the main thread

So what he does to solve the problem he mentions, is to explicitly declare .observeOn(ConcurrentDispatchQueueScheduler(globalConcurrentQueueQOS: .Background))

Assuming the API Request would be performed on background anyway, what this does is to perform all other computations in the background as well, right?

Is this a good practice? Should I, in every API request, explicitly change to background and then changes back to Main only when necessary?

If so, what would be best way? To observe on background and then on Main? Or to subscribe on background and observe on Main, as is done in this gist: https://gist.github.com/darrensapalo/711d33b3e7f59b712ea3b6d5406952a4 ?

Or maybe another way?

P.S.: sorry for the old code, but among the links I found, these better fit my question.

like image 667
Adriano Dias Avatar asked Oct 22 '18 14:10

Adriano Dias


2 Answers

Normally, i.e. if you do not specify any schedulers, Rx is synchronous.

The rest really depends on your use case. Four instance, all UI manipulations must happen on main thread scheduler.

Background work, including network requests, should run on background schedulers. Which ones - depends on priority and preference for concurrent/serial execution.

.subscribeOn() defines where the work is being done and .observeOn() defines where the results of it are handled.

So the answer to your specific questions: in case of a network call which results will be reflected in UI, you must subscribe on background scheduler and observe on main.

You can declare schedulers like that (just an example):

static let main = MainScheduler.instance
static let concurrentMain = ConcurrentMainScheduler.instance

static let serialBackground = SerialDispatchQueueScheduler.init(qos: .background)
static let concurrentBackground = ConcurrentDispatchQueueScheduler.init(qos: .background)

static let serialUtility = SerialDispatchQueueScheduler.init(qos: .utility)
static let concurrentUtility = ConcurrentDispatchQueueScheduler.init(qos: .utility)

static let serialUser = SerialDispatchQueueScheduler.init(qos: .userInitiated)
static let concurrentUser = ConcurrentDispatchQueueScheduler.init(qos: .userInitiated)

static let serialInteractive = SerialDispatchQueueScheduler.init(qos: .userInteractive)
static let concurrentInteractive = ConcurrentDispatchQueueScheduler.init(qos: .userInteractive)

P.S. Some 3rd-party libraries may provide observables that are pre-configured to execute on a background scheduler. In that case explicitly calling .subscribeOn() is not necessary. But you need to know for sure whether this is the case.

And a recap:

  • normally, should I explicitly switch to a background thread when performing network requests?. - yes, unless a library does it for you

  • Should I, in every API request, explicitly change to background and then changes back to Main only when necessary? - yes

  • If so, what would be best way? [...] subscribe on background and observe on Main

like image 199
Maxim Volgin Avatar answered Oct 23 '22 23:10

Maxim Volgin


You are right. Of course the actual network request, and waiting for and assembling the response, is all done on a background thread. What happens after that depends on the network layer you are using.

For example, if you are using URLSession, the response already comes back on a background thread so calling observeOn to do anything other than come back to the main thread is unnecessary and a reduction of performance. In other words, in answer to your question you don't need to change to a background thread on every request because it's done for you.

I see in the article that the author was talking in the context of Alamofire which explicitly responds on the main thread. So if you are using Alamofire, or some other networking layer that responds on the main thread, you should consider switching to a background thread if the processing of the response is expensive. If all you are doing is creating an object from the resulting dictionary and pushing it to a view the switch in context is probably overkill and could actually degrade performance considering you have already had to suffer through a context switch once.

I feel it's also important to note that calling subscribeOn is absolutely pointless for either network layer. That will only change the thread that the request is made on, not the background thread that waits for the response, nor the thread that the response returns on. The networking layer will decide what thread it uses to push the data out and subscribeOn can't change it. The best you can do is use observeOn to reroute the data flow to a different thread after the response. The subscribeOn operator is for synchronous operations, not network requests.

like image 4
Daniel T. Avatar answered Oct 23 '22 23:10

Daniel T.