If you have been playing around with Swift's Combine you probably reached a point where you need to create your own custom Publisher. Besides the implementation details, I will do my best to share my thought process when doing it. A lot of help comes from the work done by Sergej Jaskiewicz at OpenCombine on Github. At most what I am doing in this post, is to share how you could do these implementations on your own, instead of digging through the OpenCombine repo.

Given that this is an experiment, if you spot a mistake, or have any feedback, let me know.

The first thing you need to understand with Combine, if you are coming from ReactiveSwift/ReactiveCocoa, is that operations (e.g. map) are represented by an entity that encapsulates it. This approach means that if you are creating your own func myOperation that works with other Publishers you will also have to create a ย struct MyOperation. ย 

Before jumping to the code, there are three main concepts, that we will use throughout these examples:

  1. Publisher
  2. Subscriber
  3. Subscription

These three concepts are quite well definied and follow the Reactive-Stream spec, which I advise reading. You will see that I have skipped some of the steps, described in the spec. The goal of this post is besides implementing your custom Publisher, for you to understand how these three entities work together.

Finally, I will also mention the concept of Upstream and Downstream when relevant. I followed Sergej's suggestion regarding naming, since they map well to my own mental model and most importantly how the data flows.

This is well pictured here:

Let's start with the case when you want to create an entity, but without any operation. An example of such a case can be Just. This Publisher, is initialized with a single value, that is sent once the subscription happens. In order to avoid conflicts with the original Just provided by Combine, let's call it MyJust:

The significant bits are at line 16, 17. This is where we make a connection between our MyJust and whatever subscriber that might be interested in its values.

At line 16, once you get a hold of the Downstream, you specify how the Subscription should work, by creating your own implementation with MyJustSubscription. You pass the Downstream and the actual value at line 30.

At line 35 is where you define the MyJust behaviour, by stating what should happen when something (or Downstream) demands a value:

  1. You check that the demand is bigger than zero.
  2. You send the value via receive(value).
  3. You complete the signal via receive(completion: .finished).
  4. You let go off the Downstream, since the work is done.

Going back to line 17, you pass your custom subscription to the downstream via subscriber.receive. In essence: the Subscriber, demands ย values from the Publisher, via a Subscription.

By now you migth be thinking: ok, so how do I demand a value?

A simple sink can and will do the job:

In the next parts, we will further explore this concept. Any question hit me on twitter.