Combine: switchToLatest

Combine: switchToLatest

With this short reference code snippet, you will be able to use the convenient switchToLatest() operator when configuring subscribers in Combine.

This brief overview will demonstrate some basic features that may come in handy when working with publishers in Combine, Apple's framework to handle asynchronous events by combining event-processing operators. The Publisher protocol declares a type that transmits a sequence of values over time that subscribers can receive as input by adopting the Subscriber protocol.

A common use case would be that multiple requests for asynchronous work may be issued, but only the latest request is really needed. For example, requests could be aimed at loading images from a remote source. A user might tap on a button to load the image but may decide to move on and tap on another button to load another image before the first request is even completed. In this case, subscriptions in Combine can be configured with a .switchToLatest() operator to receive a new publisher from the upstream publisher and cancel its previous subscription.

For example, you could configure a subscription to always use the latest values, dropping any intermediate requests when loading images in the abstract getImage() below:

import Foundation
import UIKit
import Combine

let images = ["lemon.jpg", "pineapple.jpg", "strawberry.jpg"]
var index = 0

func getImage() -> AnyPublisher<UIImage?, Never> {
    return Future<UIImage?, Never> { promise in
        //simulate delay for download
        DispatchQueue.global().asyncAfter(deadline: .now() + 3.0) {
            promise(.success(UIImage(named: images[index])))
        }
    }.print().map { $0 }
    .receive(on: RunLoop.main)
    .eraseToAnyPublisher()
}

let taps = PassthroughSubject<Void, Never>()

let subscription = taps.map { _ in getImage() }
    .switchToLatest().sink {
        print($0)
    }

Now, whenever a taps is triggering to signal that an event has occurred with the .send() function, the getImage() function is run with an articifucal delay of 3 seconds to simulate the behavior of downloading data from a remote source.

//get the lemon image
taps.send()

If more events are scheduled with slight delays, e.g. an event to load the pineapple image with a 6 second delay and and event to load the strawberry image with a 6.5 second delay, the request for the pineapple image will not be ceompleted before the strawberry request is issued. As a consequence, the pineapple request will be cancalled the the new strawberry request will be received due to the .switchToLatest() in the subscription.

//get the pineapple image
DispatchQueue.main.asyncAfter(deadline: .now() + 6.0) {
    index += 1
    taps.send()
}

//get the strawberry image
// overwrites the pineapple due to switch to latest
DispatchQueue.main.asyncAfter(deadline: .now() + 6.5) {
    index += 1
    taps.send()
}

xc

The.print() operator in the publishing pipeline allows the process to be easily followed in the console.

receive subscription: (Future)
request unlimited
receive value: (Optional(<UIImage:0x600000bcc090 named(lemon.jpg) {1920, 1920}>))
receive finished
Optional(<UIImage:0x600000bcc090 named(lemon.jpg) {1920, 1920}>)
receive subscription: (Future)
request unlimited
receive cancel
receive subscription: (Future)
request unlimited
receive value: (Optional(<UIImage:0x600000be0900 named(strawberry.jpg) {1920, 1929}>))
receive finished
Optional(<UIImage:0x600000be0900 named(strawberry.jpg) {1920, 1929}>)

All in all, .switchToLatest() is a powerful operator to avoid unnecessary background work and confusing app behavior by always subscribing to the lastest and most up to date request for work to be done asynchronously.


Where to go next?

If you are interested into knowing more about working with Combine and the various operators available to shape the sequence of values send by publishers and received by subscribers, check our other articles on Combine that will be released over the next days and weeks.

They will cover how to work with filters, sequence and transformation operatators or timers, how to use the dataTaskPublisher, how to combine publishers as well as how to debug Combine messages and much more.

To be notifed when new material is out, join our free mailing list.