Combine: Working with Transformation Operators

Combine: Working with Transformation Operators

With these short code snippets you will be able use transformation operators when configuring publisher 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.

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 transformation operators.

You can use the .collect(_ count: Int) operator to return arrays of values, in this case chunked in groups of 3 elements.

import Foundation
import Combine

["A","B","C","D","E","F","G","H"].publisher.collect(3).sink {
    print($0)
}

With help of formatters you can use the .map operator to transform elements, for example from numbers to strings:

let formatter = NumberFormatter()
formatter.numberStyle = .spellOut

[123,45,67].publisher.map {
    formatter.string(from: NSNumber(integerLiteral: $0)) ?? ""
}.sink {
    print($0)
}

You can also use .map with KeyPath, for example:

struct Point {
    let x: Int
    let y: Int
}

let publisherA = PassthroughSubject<Point, Never>()

publisherA.map(\.x, \.y).sink { x, y in
    print("x is \(x) and y is \(y)")
}

publisherA.send(Point(x: 2, y: 10)) //x is 2 and y is 10

Another very convenient operator is .replaceNil<T>(with output: T), which replaces nil and can be used with .map to unwarp optionals:

["A","B",nil,"C"].publisher.replaceNil(with: "*")
    .map { $0! }
    .sink {
        print($0) //A B * C
}

You can also use the.scan method to scan through a sequence of values and transform them one by one to match your use case.

let publisherB = (1...10).publisher

publisherB.scan([]) { numbers, value -> [Int] in
    numbers + [value]
}.sink {
    print($0)
}
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

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.