A brain dump on Combine

My learnings on Combine. Shared with you.

It’s recommended that you have a basic understanding of these topics

  1. Functional programming in Swift

Contents

  1. Publishers
  2. Subscribers and Subscriptions
  3. Operators
    1. filter
    2. compactMap
    3. replaceNil
    4. map

Publishers

A Publisher publishes values to interested parties known as Subscribers. The publisher alerts its subscribers that new values are available but it’s upto the subscriber to actually grab those new values. This serves to alleviate backpressure.

The following creates a Publisher that publishes Integer values and never sends any errors:

let publisher = PassthroughSubject<Int, Never>()

If nobody is subscribed then no values are published.

Subscribers & Subscriptions

A Subscriber subscribes to a Publisher to receive values from it over time.

.sink can be called on a Publisher to create a closure-based Subscriber:

let publisher = PassthroughSubject<Int, Never>()

publisher
    .sink { value in
        print("Recieved:", value) // receives: 1
    }

publisher.send(1) // send the integer value 1 to any Subscribers

send() can be called infitely on this type of Publisher as we have no means to cancel it. Yet.

.sink returns a token of type AnyCancellable which should be stored to keep the connection beween the Publisher and Subscriber alive. We can then call cancel() to remove the subscription:

var subscription: AnyCancellable?
let publisher = PassthroughSubject<Int, Never>()

subscription = publisher
    .sink { value in
        print("Recieved:", value) // receives: 1
    }

publisher.send(1) // send the integer value 1 to any Subscribers. Received in the .sink closure above

subscription?.cancel() // subscription dies

publisher.send(2) // 2 is not recieved in the .sink closure as the subscription has just been cancelled

Operators

filter

Filters-out any published values that do not meet the filter’s requirements.

The below adds the filter operator to filter any values lower than 0 so they are not passed on to the Subscribers:

var subscription: Cancellable?

let publisher = PassthroughSubject<Int, Never>()

subscription = publisher
    .filter { $0 > 0}
    .sink { value in
        print("Recieved:", value)
    }

publisher.send(2) // prints
publisher.send(1) // prints
publisher.send(0) // does not print; less than 0, filtered-out

compactMap

Removes any values of nil and passes them along the stream.

Take the following publisher of integers. It’s optional because it contains one optional value. Using compactMap, only non-nil values are allowed to be passed on down to be dealt with by filter, etc.

var subscription: Cancellable?

let publisher = [1, 2, Optional<Int>(nil), 3].publisher

subscription = publisher
    .compactMap { $0 }
    .filter { $0 > 0}
    .sink { value in
        print("Recieved:", value)
    }

subscription?.cancel()

The above example will print the following to your console:

Recieved: 1
Recieved: 2
Recieved: 3

replaceNil

replaceNil will let you remove any nil values and instead return a more useful value.

var subscription: Cancellable?

let publisher = [1, 2, Optional<Int>(nil), 3].publisher

subscription = publisher
    .replaceNil(with: 999)
    .filter { $0 > 0}
    .sink { value in
        print("Recieved:", value)
    }

subscription?.cancel()

This will replace the nil value in the array with 999 which also passes the .filter(). The console will show:

Recieved: 1
Recieved: 2
Recieved: 999
Recieved: 3

map

map will allow you to transform a value in some way.

In the below example, the integer values that pass the filter are transformed into an interpolated string using the map operator, adding an x on either side of the number:

var subscription: Cancellable?

let publisher = [1, 2, Optional<Int>(nil), 3].publisher

subscription = publisher
    .replaceNil(with: 999)
    .filter { $0 > 0}
    .map { "x\($0)x" }
    .sink { value in
        print("Recieved:", value)
    }

subscription?.cancel()

The sink above is changed from a publisher of Integers to one of Strings now:

Xcode pop-up showing summary of our publisher