Streams of values are at the heart of reactive programming. These values are delivered by publishers. In this episode, we zoom in on the relationship between publishers and subscribers.
Playing With Publishers
Fire up Xcode and create a playground. Clear the contents of the playground and add an import statement for the Combine framework at the top.
import Combine
There are many ways to create publishers. Let's keep it simple for now. We define an array with three integers, 1, 2, and 3.
import Combine
[1, 2, 3]
You learned in the previous episodes that Apple reactified a number of system frameworks to make it straightforward to integrate the Combine framework into a project. Earlier in this series, we created a publisher that delivers UIApplication.didBecomeActiveNotification notifications by invoking the publisher(for:object:) method of the NotificationCenter class.
We can ask the array literal for a publisher via its publisher computed property defined on Array. The publisher emits the elements of the array one by one.
import Combine
[1, 2, 3].publisher
We can subscribe to the publisher by invoking the sink(_:) method. We covered this earlier in this series. We print the values the publisher emits in the closure we pass to the sink(_:) method.
import Combine
[1, 2, 3].publisher
.sink { value in
print(value)
}
The output in the console confirms that the publisher delivers the values of the array literal one by one.
1
2
3
Anatomy of a Publisher
The example is as simple as it gets, but it is all we need to understand the ins and outs of the Combine framework. Let's take a moment to explore the anatomy of a publisher.
Option click the publisher computed property to inspect its type. Xcode reveals that the publisher is of type Publishers.Sequence. Publishers is an enum and serves as a namespace for types that act as publishers.

We are interested in the Sequence type. Let's dig a bit deeper and click Sequence to open the documentation. The documentation shows us that Sequence is a struct, a value type.

The most interesting piece of information can be found at the bottom. The Sequence struct conforms to the Publisher protocol. Every type that acts as a publisher needs to conform to the Publisher protocol. That is important to understand and remember.
Let's take a look at the Publisher protocol. Apple's summary of the Publisher protocol is easy to understand. We covered this several times in this series.
Declares that a type can transmit a sequence of values over time.
The Publisher protocol defines two associated types, Output and Failure. As the name suggests, Output defines the type of values the publisher delivers. The publisher we created in the playground emits integers, which means Output is equal to Int.
Failure defines the type of errors the publisher can deliver. It is no surprise that Failure is required to conform to the Error protocol. The publisher we created in the playground is a bit special because it never emits an error. Because it never emits an error, Failure is equal to Never.
So far, you learned that a publisher conforms to the Publisher protocol and that it defines an output type and a failure type. The output type defines the type of values the publisher can publish. The failure type defines the type of errors the publisher can publish. This implies that a publisher emits more than just values. A publisher can emit values as well as errors.
Anatomy of a Subscriber
Let's shift focus to the subscriber. We can subscribe to the publisher by invoking the sink(_:) method. What does it mean to subscribe to a publisher? What happens under the hood?
The sink(_:) method attaches a subscriber to a publisher. A subscriber needs to conform to the Subscriber protocol. Apple's summary of the Subscriber protocol isn't difficult to understand.
A protocol that declares a type that can receive input from a publisher.
Like the Publisher protocol, the Subscriber protocol defines two associated types, Input and Failure. As the name suggests, Input defines the type of values the subscriber can receive. The sink(_:) method creates a subscriber with an Input type that matches the Output type of the publisher it is attached to. The Input type of the subscriber and the Output type of the publisher need to match. That is a requirement.
Failure defines the type of errors the subscriber can receive. Like the Failure associated type of the Publisher protocol, the Failure associated type of the Subscriber protocol is required to conform to the Error protocol. The Failure types of the publisher and the subscriber need to match. That is another requirement.
In summary, the Output and Input types of the publisher and the subscriber need to match and the same is true for the Failure types of the publisher and the subscriber.
As I mentioned earlier, the publisher we created in the playground is a bit special because it never emits an error. Because it never emits an error, Failure is equal to Never. The Failure types of the publisher and the subscriber are both equal to Never. The sink(_:) method can only be used for publishers that have a Failure type that is equal to Never.
Terminating a Subscription
You learned in this episode that a publisher can emit values and errors. In the previous episodes, you also learned that it is possible to terminate a subscription by cancelling it. It is time to complete the picture.
A publisher can emit three types of events, a value, an error, and a completion event. A publisher emits zero or more values, but it can only emit one error or one completion event. A publisher cannot emit both an error and a completion event. Once a publisher emits an error or a completion event, it is guaranteed to not emit any other events. The subscription is terminated the moment an error or a completion event is emitted.
Let's apply this to the publisher we created in the playground. We can visualize the publisher with a marble diagram. The dots represent values and the vertical line represents a completion event. The marble diagram visualizes a publisher that terminates without errors.

This marble diagram visualizes a stream of values that terminates with an error. The dots represent values and the X represents an error.

A subscription can be terminated in one of three ways, by cancelling the subscription, by emitting an error, or by emitting a completion event. When the subscription is terminated, the resources associated with the subscription are freed up.
What's Next?
You have learned quite a bit about publishers and subscribers in this episode. Make sure you understand the concepts we covered in this episode before continuing. In the next episode, we continue dissecting the relationship between publishers and subscribers.