SwitchToLatest is a very interesting Operator that we use in our development. It receives Publisher and prints the exact value, as shown below:

Looking closely at the figure above, you can see that when a new Publisher is received, the switchToLatest points to the new Publisher and cancels the previous publisher at the same time.

This feature is useful, for example, in scenarios like the following:

As the input value is updated, new network requests are constantly triggered. In this case, we want the last network request. This scenario is the best time to use switchToLatest.

There are only two core ideas to keep in mind about switchToLatest:

  • The input is Publihser and the output is a concrete value, which means that it will wait for the output from publisher, regardless of whether publisher is immediate (Just(1)) or asynchronous (URLSession.shared.dataTaskPublisher(for: url))
  • Only the last Publisher is retained, and previous publishers are automatically cancelled

Next, we use an official example to further illustrate.

let subject = PassthroughSubject<Int.Never>()

cancellable = subject
    .setFailureType(to: URLError.self)
    .map() { index -> URLSession.DataTaskPublisher in
        let url = URL(string: "https://example.org/get?index=\(index)")!
        return URLSession.shared.dataTaskPublisher(for: url)
    }
    .switchToLatest()
    .sink(receiveCompletion: {
        print("Complete: \ [$0)")
    }, receiveValue: { (data, response) in
        guard let url = response.url else {
            print("Bad response.")
            return
        }
        print("URL: \(url)")})for index in 1.5 {
    DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(index / 10)) {
        subject.send(index)
    }
}
Copy the code

The print result is as follows:

URL: https://example.org/get?index=5
Copy the code

In the above example, we use PassthroughSubject to send data, and then use map to convert 1 through 5 to network requests. Because network requests have a certain delay, only the last sent data is output, because the previous requests have been canceled. The above example demonstrates the meaning of switchToLatest.

So what if the publisher in the example above is not asynchronous?

cancellable = subject
    .setFailureType(to: URLError.self)
    .map() { index -> Just<Int> in
        Just(index)
    }
    .switchToLatest()
    .sink(receiveCompletion: {
        print("Complete: \ [$0)")
    }, receiveValue: { value in
        print("Value: \(value)")})for index in 1.5 {
    DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(index / 10)) {
        subject.send(index)
    }
}
Copy the code

The printed result is:

Value: 1
Value: 2
Value: 3
Value: 4
Value: 5
Copy the code

So far, we have basically covered the use of switchToLatest, which corresponds to flatMap, which returns a Publisher and flows downstream when the publisher has value. FlatMap is also a very interesting Operator. Let’s change the above code slightly:

let subject = PassthroughSubject<Int.Never>()

cancellable = subject
    .setFailureType(to: URLError.self)
    .flatMap { index -> URLSession.DataTaskPublisher in
        let url = URL(string: "https://example.org/get?index=\(index)")!
        return URLSession.shared.dataTaskPublisher(for: url)
    }
    .sink(receiveCompletion: {
        print("Complete: \ [$0)")
    }, receiveValue: { (data, response) in
        guard let url = response.url else {
            print("Bad response.")
            return
        }
        print("URL: \(url)")})for index in 1.5 {
    DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(index / 10)) {
        subject.send(index)
    }
}
Copy the code

The print result is as follows:

URL: https://example.org/get?index=5
URL: https://example.org/get?index=1
URL: https://example.org/get?index=2
URL: https://example.org/get?index=3
URL: https://example.org/get?index=4
Copy the code

The order of printing depends on the speed of each network request, with the faster ones coming back first.

So to sum up,The core idea of switchToLatest is to preserve the last Publisher, which is particularly useful in real-world development for filtering redundant network requests for the search box.