How to Create Custom Publishers in Combine
If you really need them

As you’ve seen during the Combine learning path, publishers and subscribers cooperate with each other creating a powerful tool to exchange information inside your application.
Maybe the publishers provided by the framework will be enough for your work, but what about custom publishers? Is it possible to create custom ones and, if yes, how to do it?
Of course, it is, and it’s not really complex as it may seem at first. First of all, in order to create a working stream, we need three things:
- Publisher
- Subscription
- Subscriber
Using Combine, you see mostly publishers and subscribers — but the real magic behind the communication is made possible by subscription.
In fact, the publisher creates a subscription with all the required data and gives it to the subscriber.
Once the subscriber receives the subscription and real communication between the entities are created, the subscription starts its engine processing values over time.
The Subscription Mechanism
Let’s see how the three-component works and interacts with each other. This will simplify our understanding of custom publishers because we’ll have clear what is needed in order to make the asynchronous communication work.

- A subscriber subscribes to the publisher. This is usually done via the
sink()
method, which allows the programmer to manage both the completion and the values received by the publisher. - The publisher creates a subscription and delivers it to the subscriber. The subscriber receives the subscription via the
receive(subscription:)
method. - Now that the subscriber has the subscription and a contract between it and the publisher has been made, it can request for values to the publisher. This is done by sending the number of values it wants, by calling
request(_:)
. - The subscription receives the demand and starts to process information and emit values. Those values are sent one by one using the subscriber’s
receive(_:)
method until the maximum demand is reached. - The subscriber receives values from the subscription, and, as a response, it sends a new
Demand
. Note that theDemand
that is sent adds the number of values it wants to receive upon the previous demand already sent as the number of values requested can increase or stay the same but never decrease. - Once the
Demand
is received by theSubscription
, it starts the whole processing again and emits values until the Demand is satisfied. Steps 4, 5, and 6 are repeated indefinitely.
As you may have noticed, the entire communication is pretty easy, and once understood, implementing custom publishers is a doable task.
Sometimes, implementing the Publisher protocol itself is not necessary and the same goal can be achieved in other ways:
- You can use a
PassthroughSubject
, for example, a subclass ofSubject
, to publish values by calling itssend(_:)
method - Use a
CurrentValueSubject
to publish whenever you update the subject’s value. TheCurrentValueSubject
, in fact, is a subject that wraps a single value and publishes a new element whenever the value changes. - Use the
@Published
annotation on a property so that the property gains a publisher that emits an event whenever the property’s value changes. This approach is used a lot in SwiftUI.
In case you need a custom publisher because it fits your needs, the easiest way is to create an extension to the Publisher namespace.
Let’s give it a try.
Imagine you want to create a new operator because the standard ones do not implement the functionality you need. To give a more concrete example, imagine you want to recreate an operator from RxSwift, another declarative framework for Swift that you may know.
What the operator does is: take two publishers, combines them, removes the duplicates, and emit the outputs.
The most complex thing is the method signature, but don’t worry, it’s easier than you may think. First of all, it returns an AnyPublisher
with the same output and Failure type of the current publisher you’re working on.
Tip: Using AnyPublisher as a return type is a good choice because when chaining operators, it becomes really easy to make the signature complicated.
Next, because the output from both publishers has to be compared, the Output has to adopt the Equatable protocol. Notice how the generic use is applied. We want to make it generic; otherwise, the operator loses its versatility to work with any kind of Publisher.
The body of the function is trivial: we combine the publisher with another one passed as a parameter and then call two other basic operators: removeDuplicates
and map. eraseToAnyPublisher(_:)
method allows the operator to be converted into an AnyPublisher
as previously recommended.
Have you seen how easy it is? And what about a brand new publisher?
Well, to make the example more clear, if you’re familiar with the dataTaskPublisher()
from the URLSession
class, we’ll try to recreate the same behaviour making a new publisher and a new subscription.
Please notice that you can create a new publisher and NOT a new subscription, but problems may arise because you lose the ability to cope with subscriber demands, and this makes the implementation tricky for the Combine ecosystem.
Subscription
Let’s start by creating our Subscription, extending the Publishers
enum.
As we have seen in the subscription mechanism, the Subscription object is passed from Publisher
to Subscriber
and so on. This scenario makes the class, instead of a struct, more suitable to our needs, because we want to pass it by reference instead of making copies.
In the class signature, we specify the Input
type which has to be Data
and the failure to be Error
, accordingly to what’s returned by the standard URLSession
methods.
We need a URLRequest
to work on (we could have also used a URL and make a different implementation) and a subscriber to which we pass data and errors received.
Both properties are initialised via a constructor. Of course, the subscriber has to be an Optional, because, as you may imagine, it can be attached to the Subscription or not, depending on the current state of the program.
In fact, we also need the cancel method that puts the subscriber at nil.
In sendRequest
, magic happens. We call the normal dataTask
method on the URLSession
object and bind it to the subscriber using the map function.
Publisher
Now it’s time to create a publisher. Publishers are basically a structure.
After we’ve adopted the Publisher protocol, we have to define both the Output and the Failure, in our case Data and Error.
A custom initializer is used to instantiate the URLRequest object that carries information on which server to contact and the configuration.
The receive(subscriber:)
method is the core of the Publisher
. First, in the signature, we have to make a check for the Failure and Output type, to make sure that the Subscriber
fits into the Publisher
types. A new subscription is then created and passed to the subscriber, making the subscription mechanism work!
Last but not least, to make sure the subscriber is able to receive values from the DataPublisher
, we should make a method that returns the DataPublisher
so we can apply, for example, the sink()
method and perform the operation on data emitted by the publisher.
Its usage can be something like this.
Now that you’ve understood how the subscription mechanism work and how to implement a custom publisher, you’re free to test it with other components from the iOS ecosystem!
To see all the code used in this article, visit the following page:
If you want to explore other topics, visit our repository on GitHub: