How Combine works: Publishers
Tags: swift, combine, reactive, tutorial
Published: 2024-01-18 21:53:19

Apple's `Combine.framework` provides support for _async_ and _reactive_ programming, and while Swift's latest _async_/_await_ keywords make it easy to write code that reads linear, but is asynchronous, that's not a 100% overlap with `Combine`, hence, it's still relevant.

`Combine` is not all about async programming, but about _reactive programming_, about _streaming data_ and reacting to change in data.

Basically, reactive programming is about _reacting_ to changes in _data_, or _to events_. This is done _via_ streams; _channels_ that unite our _data_ during all time and update us with any possible changes as they come.

Therefore, talking the above concepts into consideration, let's see how the map to the `Combine` API.

_Combine_ defines:

- `Publisher` - a `protocol` to which one ca _subscribe_ to receive values over time
- `Subscriber` - a client of a `Publisher`, that receives the values
- `Subscription` the binding between a publisher and subscriber
- Finally, there is the concept of an operator. Operators are just Publishers that act as intermediary between a _upstream publisher_ and a downstream publishers or a terminal subscriber. They often transform/prepare data for the end subscriber. This allows for greater modularity as these _operators_ can be reused, inserted or removed at will, thus creating an effective pipeline between the initial, upstream publisher and the end subscriber.

Let's say we want to create a `Publisher` that publishes all values from `0` to `n`, like a range, starting always from `0` and going until `n`. We'll call it `Until`.

A publisher doesn't necessarily directly create its own values. Publishers are often structs, i.e. value types, they can be created in place and aren't pass around. There is notable exception that is the `Subject` publisher.

The `Publisher` protocol is generic over two types, an `Output`, the type of data which it will yield, and a `Failure`, the type of error which it yields, should something go wrong:


```swift
protocol Publisher<Output, Failure>
```


`Until` will always yield `Int` values and there's no real good reason to encounter and error, so our `Failure` type will be `Never`.

Other than that the only required method a `Publisher` needs implement is called `receive` that receives a `Subscriber`, acknowledging it by providing back a subscription.

```swift
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
``` 

This might look complicated, all it's really saying is that the method is generic every a subscriber type and that subscribes's expected input value type needs match our expect output type, so with the failure type, makes sense?

Let's implement this:

```swift
import Foundation
import Combine

struct Until: Publisher {
    // we output integers
    public typealias Output = Int
    // we never throw errors
    public typealias Failure = Never
    
    // the end value at which stop
    let endValue: Int
    

    public init(_ endValue: Int) {
        self.endValue = endValue
    }

    public func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, Int == S.Input {
      // TODO: implement it
    }
}
```

Great! Now how do we create a subscription to give it back to our subscriber? I'll leave that for a [next episode](https://dev.to/stoiandan/how-combine-publishers-work-subscriptions-36ji). 
For now out publisher will be a bit _violent_. Normally, the subscriber receives values only after he initially demands them. After each value received, he can demand even more, or less, as he wishes.

For now, our publisher will generate his own values and send them to the subscriber, regardless if he wants or not, but in the next episode we will do this right:

```swift
    public func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, Int == S.Input {
        // a dummy subscription that ignores demands
        let subscription = Subscriptions.empty
        subscriber.receive(subscription: subscription)
        
        // send the subscriber the value immediately
        // ignoring any of his possible demands
        for val in 0...endValue {
           let _ = subscriber.receive(val)
        }
        // tell our subscriber we are done
        subscriber.receive(completion: .finished)

    }
```

How do we use this? We will need a subscriber, lucky Apple made one called `Sink`, it automatically demands an unlimited amount of data and has a _callback_ where we can use the data provided by the publisher:

```swift
// store the subscription provided by sync, 
// normally if we would not do this, it will cancel our request
var cancellables = Set<AnyCancellable>()

    Until(10).sink(receiveValue: { val in
        print("We received \(val) from publisher!")
      
    }).store(in: &cancellables)

```