Combine: Reimplementing the URLSession `dataTask` Publisher in 3 different ways

Pedro Alvarez
4 min readMar 13, 2023

--

Image from https://www.temporadalivre.com/blog/castelos-brasileiros-lindas-paisagens-no-brasil-que-recordam-a-epoca-medieval

This article focus on providing you some possible implementations of a Publisher to the URLSession API request. In Combine, a Publisher is simply an instance that emits values in an asynchronous way. As we described in this past article. There are three entities that work together to broadcast Publisher outputs to multiple receivers:

  1. Publishers: Emit asynchronous values that may be transformed, filtered delayed or replicated via operators.
  2. Subscribers: Subscribe to the publisher events and listen to all the emitted values.
  3. Subscriptions: Establish a link between the Publisher and its Subscribers saving that connection in memory and creating a channel for multiple subscribers to receive the values.

All those stakeholders are represented by protocols and their tasks should follow a specific order that makes Combine work properly. That's why Apple discourages us to implement each of the protocols, if someone who's not aware of how it should works try to implement them in the wrong way, Combine may not attend his necessities. Instead, Apple provides us the operators and pre-implemented publishers to fill all our necessities. But as we desire here to understand how it works under the hood, we are about to reimplement the URLSessionpublisher in three different secure ways.

Implementing Publisher, Subscription and Subscriber

As we did in the past article, we are about to implement each of the entities involved in the URLSession publisher and establish a communication channel between them. First, let's create a new Subscription subclass, that saves the link between a Publisher and one of its Subscribers:

As you can see, our URLSessionSubscription class saves the subscriber that just attached to the publisher, and having an instance of both URLSession and URLRequest allows the subscription to start the API request with that data and retrieve the results via closure, as we originally did before Combine existed. But now, after retrieving the response, if we get an error, we may automatically broadcast that as a completion of the Publisher or, if we get a success response, we shall send the value straight to the Subscriber via its interface methods.

As the Subscription protocol establishes, we shall have a cancel method in order to deallocate the Subscriber and avoid a retain cycle.

Now let's implement the Publisher :

The Publisher only instantiates the Subscription that will guarantee the Subscriber which is attached will receive the value and sends that to the subscriber itself in order to demand the values. Finally, we should create a new Publisher instance to our original class:

Now, we can attach any subscriber that shall receive the values from the publisher:

This is the log after creating this subscription:

Using a Future Publisher

For those who don't know, a Future publisher is used for when we want to subscribe to a block of code that emits a value(or a failure) in an asynchronous way. For example, as we are about to illustrate, we may fetch data from the web using URLSession and retrieve the response in a completion, which may use the promise closure parameter from the Future to send the results to the Publisher .

Observe how we can do that:

What's happening is basically that we are calling our former imperative method dataTask with a URLRequest instance to fetch data from the Web. Every time we get a response, may it be an error or a success response, we use the promise closure from the Future to emit that asynchronous output through the Publisher. This is the best way to return an asynchronous output from an imperative function as a Combine Publisher. It shall be used in other use cases as well, but we focus on our URLSession scenario.

Use a PassthroughSubject

Instead of a Future in order to return an asynchronous value you may rely on a Subject to that. What you should do is just create a PassthroughSubject that returns the output of the API call and return it in the main thread as an AnyPublisher . The class that calls it will receive the Publisher and subscribe to it, and as soon as the API returns some response, it may be sent through the PassthroughSubject .

All the outputs are sent through the subject and may be subscribed.

Conclusion

It's very important to understand how our most used frameworks and APIs work under the hood and how are their implementation. That facilitates our usage and turns us into better developers. We provided three different ways to implement a Publisher to URLSession that may also be applied to create publishers to any kind of asynchronous data resource. I hope you are now ready to deliver values via Combine in any of your own libraries and enjoyed it ;).

--

--

Pedro Alvarez
Pedro Alvarez

Written by Pedro Alvarez

Mobile Engineer | iOS | Android | KMP | Flutter | WWDC19 scholarship winner | Blockchain enthusiast https://www.linkedin.com/in/pedro-alvarez94/

No responses yet