Reactive Programming in iOS with Combine

Feb 4 2021 · Swift 5.3, macOS 11.0, Xcode 12.2

Part 4: Timing, Scheduling and Sequencing Operators

27. Scheduling Operators

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 26. Challenge: Collect Values by Time Next episode: 28. Sequencing Operators

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Heads up... You've reached locked video content where the transcript will be shown as obfuscated text.

Certain operators in Combine take in a scheduler as a parameter. Most of the time, this is DispatchQueue.main, but it can vary. What is a scheduler in this case? Per Apple's documentation, the scheduler is a protocol that defines when and how to execute a closure. And this is most, but not all of the story. The word scheduler implies the time that the closure is executed, either now or in the future. What's missing is where the closure is executed. There is no reference to which thread the closure will be executed on. This is left to the concrete implementation of the scheduler you choose. So the key message to take home here. A scheduler is not equal to a thread. Here's what a scheduler looks like from an event flow standpoint. A button press occurs on the main thread. Some process fires off in a background scheduler and the results are sent back to the main thread, where the apps UI is updated. In this episode you'll learn about two operators related to subscribers, subscribe and receive. The subscribe(on:) and subscribe(on:options) operators create the subscription or start the work on the specified scheduler. Back in the first part of the course you learned that a publisher is inanimate until something subscribes to it. What happens when that takes place? First, publisher receives the subscriber and creates a subscription. Then subscriber receives the subscription and request values from the publisher. These are the dotted lines in the diagram. Then the publisher starts work via the subscription. And then the publisher emits values via that subscription. Once that happens, operators transform values, where applicable. And finally, the subscriber receives the final value. Steps one, two, and three typically happen on the thread that is current when the publishers get the subscription. However, when you use the subscribe(on:) operator all these operations run on the scheduler you specified. An example of using subscribe(on:) is when you want the publisher to perform an expensive computation and you want to avoid blocking the main thread. Let's take a look at this in an example. Open the Starter Playground for this episode and select the subscribeOn-receiveOn page. Make sure the debug area is displayed. Add an expensive publisher, which is provided by the Publishers.ExpensiveComputation duration method in the Playground's supplemental source code. This simulates a long running computation that emits a string after the specified duration. Then create a serial queue that you'll use to trigger the computation on a specific scheduler. DispatchQueue adopts the scheduler protocol, so this works great for our demo. Next, obtain the current thread number, thanks again to an extension on thread provided in the Playground's supplemental source code. Now make a subscription to the computation publisher. Use a sink to display the value the computation publisher emits. Run the Playground and examine the output in the debug area. The code is running on thread one, which is the main thread. It subscribes to the publisher here. The publisher receives a subscriber. It creates a subscription and then starts work. When the work is complete, the publisher delivers the result through the subscription and completes. Insert a subscribe(on:) operator and use the queue we created for the parameter. Run the Playground again. The code still starts on the main thread. This time, however, the publisher subscriber process happens on thread four, which is one of the threads from the queue you made. Note that you may see a different number when you run the Playground. Just note it is different from one, which is the main thread. The receive(on:) and receive(on:options), on the other hand, deliver values on the specified scheduler. What does this mean? What if you wanted to update your UI with updated values after this subscription completes? Typically you would have to dispatch that code back to the main thread with something like DispatchQueue.main.async, but the receiver operator can take care of that for you. Let's go back to the Playground for an example. Continue with the previous demo and insert a receive operator after the subscribe operator, using the main queue as the parameter here. And run the Playground. The difference here is that the computation results are received back on thread one, the main thread, instead of the background thread. With this value in hand on the main queue, you can update your user interface components safely. In this episode you learned about two scheduling operators in Combine. subscribe(on:) and receive(on:). Subscribe(on:) causes the subscription to the publisher to occur on whatever scheduler you specify, and receive(on:) allows you to deliver values from the background thread to the main thread, so you can, for example, update your user interface safely. In the next episode, you'll learn about sequence related operators in Combine. And I'll see you then.