Swift Combine: Create your own Subscriber

KD Knowledge Diet
4 min readOct 4, 2022

Looking to clean up your subscriptions? You can make a separate file and a subscriber! This is how you do it.

Subscriber Protocol

class StringSubscriber: Subscriber {

}

Let’s go ahead and create subscriber class and I will call it StringSubscriber.

Make your class conform to Subscriber protocol.

Now, you can obviously create a new file and you can do that over there.

But since you are simply just learning about creating your subscribers, I’m going to create in the ViewController.swift file. But in your actual application, you can create a separate file.

Typealias from Subscriber

class StringSubscriber: Subscriber {

typealias Input = Type
typealias Failure = Type

}

When your class conforms to Subscriber protocol, you can see two typealias.

  • Input means that this particular string subscriber is going to be working on this kind of datatype.
  • Failure means what kind of error you expect from the subscriber.
class StringSubscriber: Subscriber {

typealias Input = String
typealias Failure = Never

}

For the input type, I specified String data type because StrinbSubscriber is going to handle subscription from the publisher as String Values.

For the failure, I specified Never, which means that it’s never going to fail.

Methods

class StringSubscriber: Subscriber {

typealias Input = String
typealias Failure = Never

func receive(subscription: Subscription) {

}

func receive(_ input: String) -> Subscribers.Demand {

}

func receive(completion: Subscribers.Completion<Never>) {

}

}

When conforming to Subscriber protocol, so there are three different functions that you need to provide or implement in your StringSubscriber.

  • func receive(subscription: Subscription) gets subscription.
  • func receive(_ input: String) -> Subscribers.Demand gets the value.
  • func receive(completion: Subscribers.Completion<Never>) gets the completion.

Before go ahead, let’s look at the relationship between a publisher and a subscriber.

Publisher Subscriber Relationship

Publisher Subscriber

So, by looking at this diagram, you can see the relationship between a publisher and a subscriber.

Step 1: subscriber subscribes to a publisher.

Step 2: Then, the publisher creates subscription, and gives it back to the subscriber.

Step 3: The subscriber now can request the values from the publisher.

Step 4: The publisher can return values.

Step 5: Finally, the publisher sends a completion when the values are completed.

That’s the normal flow between the publisher and the subscriber.

So now you have a little bit of idea of how the things are flowing between the publisher and the subscriber.

1. func receive(subscription: Subscription)

// When the subscriber is going to receive the subscription
func receive(subscription: Subscription) {
print("Received Subscription")
}

When you are receiving a subscription, you can also request that how many items you want to receive.

// When the subscriber is going to receive the subscription
func receive(subscription: Subscription) {
print("Received Subscription")
subscription.request(.max(3))
}

I set the limit of receiving only three items.

Whenever you’re setting the maximum items, you are telling publisher that “Hey, I’m only interested in three items, so don’t send me more.”

This thing is called the backpressure.

  • Backpressure simply means that your publisher might be sending you one hundred items or a lot of data, but you’re specifying how many items you want to receive from the publisher.

func receive(_ input: String) -> Subscribers.Demand

func receive(_ input: String) -> Subscribers.Demand {

}

This method is where you are actually receiving the value.

func receive(_ input: String) -> Subscribers.Demand {
print("Received Value", input)
return .none
}

There are multiple options for this method. You can override func receive(subscription: Subscription) you set previously. For example, if you return unlimited from this method, you are actually overriding func receive(subscription: Subscription).

Overriding receive(subscription: Subscription)

// When the subscriber is going to receive the subscription
func receive(subscription: Subscription) {
print("Received Subscription")
subscription.request(.max(3)) // backpressure
}

// Where you receives the acutal value.
func receive(_ input: String) -> Subscribers.Demand {
print("Received Value", input)
return .unlimited
}

By doing this, the option you made on func receive(subscription: Subscription) will be ignored.

Not overriding receive(subscription: Subscription)

// When the subscriber is going to receive the subscription
func receive(subscription: Subscription) {
print("Received Subscription")
subscription.request(.max(3)) // backpressure
}

// Where you receives the acutal value.
func receive(_ input: String) -> Subscribers.Demand {
print("Received Value", input)
return .none
}

If you are returning .none from func receive(_ input: String) -> Subscribers.Demand, subscription.request(.max(3)) will remain valid.

func receive(completion: Subscribers.Completion<Never>)

func receive(completion: Subscribers.Completion<Never>) {
print("Completed")
}

This is going to be sent to the subscriber when the publisher is finished publishing the event.

Use your custom subscriber

let publisher = ["A", "B", "C", "D", "E", "F"].publisher
let subscriber = StringSubscriber()

/*
publisher.sink { _ in
print("completion")
} receiveValue: { value in
print("value")
}
*/

publisher.subscribe(subscriber)

Instead of sink, now you can use your custom subscriber.

Conclusion

  1. Conform to Subscriber
  2. Specify data type you are handling with and error type
  3. Customize your subscription with func receive(subscription: Subscription) and func receive(_ input: String) -> Subscribers.Demand
  4. Handle Data with func receive(_ input: String) -> Subscribers.Demand
  5. Handle Completion with func receive(completion: Subscribers.Completion<Never>)

--

--

KD Knowledge Diet

Software Engineer, Mobile Developer living in Seoul. I hate people using difficult words. Why not using simple words? Keep It Simple Stupid!