Combine: Working with Sequence Operators

Combine: Working with Sequence Operators

With these short reference code snippets, you will be able to use sequence operators when configuring publishers with 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.

Using a sequence as values to publish, Combine allows typical operators available in Swift to shape the values that will be published or received. For example, publishers can be used with sequence operators.

Here, the sequence of values is checked for its maximum value with the .max() function, adopting the Comparable protocol. As a result, the highest value of the sequence is sinked. It works similarly with the .min() function to sink the lowest value of the sequence.

import Foundation
import Combine

//min and max
let publisherA = [1,-45,3,45,100].publisher

publisherA.max().sink {
        print($0) //100

For convenience, you can also use the .first() or .last() function to only sink the first or last value of a sequence.

let publisherB = ["A","B","C","D"].publisher

publisherB.first().sink {
    print($0) //A

publisherB.last().sink {
    print($0) //D

publisherB.first(where: { "Cat".contains($0) }).sink {
    print($0) //C

With the .output(at index: Elements.index) function, you can specify the index within the sequence and sink the associated value. It also works with a range of indices to sink all associated values, for example:

let publisherC = ["A","B","C","D"].publisher

publisherC.output(at: 2).sink {
    print($0) //C

publisherC.output(in: (0...2)).sink {
    print($0) //A B C 

With the .count() function, you can track, how often a publisher emitted values, for example:

let publisherCountD = ["A","B","C","D"].publisher
publisherCount.count().sink {
    print($0) //4

You can also check, whether a sequence contains a specific value with the .contains(_ output: Elements.Element) function, which will return a bool. For example:

let publisherE = ["A","B","C","D"].publisher

publisherE.contains("F").sink {
    print($0) //false

You can also use the .allSatsify(_ predicate: (Output) -> Bool) function that takes a predicate as input with conditions that must be met by all values and returns a bool. For example:

let publisherF = [12,2,14,4,6,8].publisher

publisherF.allSatisfy { $0 % 2 == 0 }.sink { allEven in
    print(allEven) //true

Another nice sequence operator is .reduce<T>(_ initialResult: T, _ nextPartialResult @escaping (T, Output) -> T) which summarises values in a sequence into one accumulated value. For example:

let publisherG = [1,2,3,4,5,6].publisher

publisherG.reduce(0) { accumulator, value in
    print("accumulator: \(accumulator) and the value is \(value)")
    return accumulator + value
}.sink {
    print($0) //21

All in all, sequence operators provide ample opportunities to customize the publisher output to your liking. Of course, these are just a few examples and there are many others to choose from.

Where to go next?

If you are interested in 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 operators or timers, how to use the dataTaskPublisher, how to combine publishers as well as how to debug Combine messages, and much more.