Visualize Combine Magic with SwiftUI – Part 2

Operators, subscribing, and canceling in Combine

Kevin Cheng
Oct 23, 2019 · 3 min read
Photo by Aziz Acharki on Unsplash

Perform Combine Operations in our Playground

In the end of this chapter, we’ll have a list of Combine operators (map, filter, scan and dropFirst) in master/detail format. You’ll be able to select one of them and start subscribing and canceling at anytime.

Before we dive into more Combine operators, we need to modify our playground a little bit. In the previous chapter, we displayed only one StreamView at a time. Now we’d like to add another StreamView right below the existing one to compare side by side.

Below is a new view we created to present the map operator.

CombineMapStreamView

First, we add an additional stream and have them working side by side.

@State private var stream1Values: [String] = []@State private var stream2Values: [String] = []

Streams 1 and 2 are the source of truth of the two streams emitting values on the SwiftUI view.

@State private var disposables = Set<AnyCancellable>()

Remember AnyCancellable? Now that we have two streams to subscribe to, we need to keep two instances of AnyCancellable in memory.

Since we plan to keep both streams receiving and completing (or canceling the subscription) at the same time, we keep them in a set (kind of like RxSwift’s DisposeBag). AnyCancellable has this convenient extension method store, so you can keep the code clean.

publisher.sink {
self.stream1Values.append($0)
}.store(in: &self.disposables)

As you can see, AnyCancellable is being stored in the disposables set.

Now, let’s take a look at the publishers.

let publisher = self.invervalValuePublisher()

The method intervalValuePublisher emits values every second from 1 to 5 in sequence. If you haven’t read Part 1, you can find more details there.

let mapPublisher = publisher.map { (Int($0) ?? 0) * 2 }.map { String($0) }.eraseToAnyPublisher()

MapPublisher is the main character in this example. It multiplies each emitted value by 2 from the publisher.


Simple, right? Now I can easily demonstrate another operator, scan, with this CombineScanStreamView.

let scanPublisher = publisher.map { Int($0) ?? 0 }.scan(0) { $0 + $1 }.map { String($0) }

The first two maps you see here are simply casting types between the string and the integrator. We use scan to observe every value within the stream and add them up to the next value (similar to Swift Array’s reduce method)

As you see, we can now apply the same rules to create views for all the other operators: filter, drop, merge, FlatMap, etc.


Organizing Combine Operators

Now that we know how to visualize Combine operators, we want to be able to organize them a little bit.

Ideally, there should be a list of operators to choose from, and we should be able to just navigate with SwiftUI’s awesome NavigationView and drill down to check out each operator. However, I don’t want to create new views for every operator. Let’s reuse one CombineStreamView and pass a publisher with any operator we want for the second stream to perform.

Let me create another view named GenericCombineStreamView.

var comparingPublisher: (AnyPublisher<String, Never>) -> (AnyPublisher<String, Never>)

As you can see, this view takes a closure that’s in charge of modifying the publisher (with operators).

let comparingPublisher = self.comparingPublisher(publisher)
comparingPublisher.sink {
self.stream2Values.append($0)
}.store(in: &self.disposables)

We get the operated publisher by passing the original publisher to the closure. Then subscribe as before.


Put Together in SwiftUI List

Let’s put them together and add a few more operators.

func filterPublisher(publisher: AnyPublisher<String, Never>) -> AnyPublisher<String, Never> {
publisher.filter { $0 != "2" }.eraseToAnyPublisher()
}
func dropPublisher(publisher: AnyPublisher<String, Never>) -> AnyPublisher<String, Never> {
publisher.dropFirst(2).eraseToAnyPublisher()
}

In addition to the existing map and scan operators, we’ve added examples for filter and drop.

Now we’ve got a list of all the examples in one place.

Here’s the source code.


Next Chapter

We’ve successfully created a playground that’s extendable enough to present a list of single-operation streams. But what about multi-operation streams (e.g., map and then filter)? Or how about single-operation streams from multiple streams (e.g., merging two streams).

In the next chapter, we’ll expand our playground again to accommodate those operators.

Better Programming

Advice for programmers.

Kevin Cheng

Written by

iOS engineer / entrepreneur / freelancer / San Francisco

Better Programming

Advice for programmers.

More From Medium

More from Better Programming

More from Better Programming

More from Better Programming

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade