Integrating Apple Combine Framework Into Your Existing iOS Project

Ahmed Nasser
The Startup
Published in
6 min readDec 11, 2019

Combine is a new framework by Apple introduced at WWDC 2019. “The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.” Apple Developer documentation.

The new Combine framework released by Apple in the new iOS 13 and Xcode 11 is quite cryptic. As a new framework it was very Challenging for me to reach an approach of integrating combine into my existing project that i can be satisfied about .

This article shows how to integrate combine into an existing iOS Project safely .

This article exploring the following :

  • Using Deferred that awaits subscription before running the supplied closure to create a publisher for the new subscriber
  • Using Future Publisher to produce a promise with single value and then finishes or fails.
  • Make Future Publisher Cancellable so we can cancel the observation any time we want

So Let’s Get Started

So i had an iOS Project that searches in flicker Photos using FlickerAPI .

The App is built with clean architecture which is very similar to VIPER architecture also the network layer is well separated which made my job much easier.

let’s revisit some concepts about Combine as we go through this article :

Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.

  • The Publisher protocol declares a type that can deliver a sequence of values over time.
  • At the end of a chain of publishers, a Subscriber acts on elements as it receives them. Publishers only emit values when explicitly requested to do so by subscribers.

You can subscribe to a Publisher using sink operator :

I had the SearchAPIWorker that gets the search data from the Flicker Search API as a Hydra Promise “Don’t get confused with this and Combine Future Promise” .

So I wanted to use combine in my View but i faced The Problem that i needed to pass the view a Combine Publisher instead of a Hydra Promise .

I needed to change the return value to A Publisher Object, But How I can do that Knowing that Publishers doesn’t support creating a new Publisher Object wrapping a closure at least after Xcode 11 Beta 3.

Here comes Convenience Future Publisher to the Rescue .

Combine Future is pretty similar to the promise/future in other programming languages . The Future can be used to provide Result using a closure. The closure will be invoked with one parameter which is (Result<Output, Failure>) -> Void using Promise as typealias.

Now we can get the view a Publisher it wanted to begin integrating with Combine so the new method will be like this :

Let’s Explore what i’ve done by Lines :

  1. I’ve changed the return type from Hydra Promise to Future<Any,Error>

Any represents the returned data type

Error is the returned error which will be returned with two status either Finished or Error Object

2. Here i created the Future Object which will return a Publisher wrapping the closure i want to execute .

6 . Here i returned the Future.Promise wrapping the Object i wanted to pass to the view

Future.Promise Type Alias is a type that represents a closure to invoke in the future, when an element or error is available. it has to states either success or failure

But Then I Faced Two Critical Problems

I . There is some particular behavior from Combine.Future:

Future is a reference type that invokes the passed closure as soon as it is created (i.e. the closure gets invoked before any subscriber has subscribed)
This was surprising to me since I was expecting the closure to execute per-demand when a subscriber subscribes (as most other operators).

Once the Future's closure call its promise, the result is stored to deliver to the current and future subscribers.” https://forums.swift.org/u/dehesa

Which was very critical that the Future will execute the network call immediately upon creation which what i didn’t want to .

Instead i needed to execute the closure only when subscribe to the publisher.

Here comes the second Convenience Publisher the Deferred Publisher .

Which is a Publisher that awaits subscription before running the supplied closure to create a Publisher for the new subscriber.

Using Deferred Publisher I grantee that A new Future instance will be created every time a subscriber subscribes only.

Now it’s the time to change my method again :

Here we changed the return type from Future<Any,Error> to Deferred<Future<Any,Error>> .

The Deferred Publisher wraps a Publisher which is convenient since we already created a Future Publisher .

Now I can Subscribe to the returned Publisher in the view interactor Like this :

Here i subscribed to A publisher returned from apiWorker Object using sink Operator let’s take some notes about what we have here

  1. receiveCancelCompletion:will always be invoked after the publisher finishes publishing the value, it passes the completion enum that we can use to check whether it finished with error or not. :
  • in case of the sent is a success promise it will be Subscribers.Completion<Error>.finished
  • in case of the sent is a success promise it will be Subscribers.Completion<Error>.failure

2. Subscription is an Cancellable that means it will be cancelled and cleared from the function scope immediately so you need to store the subscription to make sure that the publisher outlives the function .

II. The second problem i faced is that the Future Publisher cannot be cancelled it means that i won’t be able to stop the task if i wanted .

Unfortunately there’s no direct way to do this but we can check for it manually like this :

Now whenever we call deferred.cancel() the receiveCancelCompletion: will execute changing the bool type to cancel which will we check in the Future Promise block in order to whether cancel the operation or not.

Note That : Now the type of deferred object is not Deferred<Future<Any,Error>>, But Deferred<HandleEvents<Future<Any,Error>>>

Congratulation now you have integrated combine into your existing app 😋!

You can download the final project from the GitHub repository below.

Checkout CombineWrapper Branch

Conclusion

The Combine framework can be compared to frameworks like RxSwift and ReactiveSwift (formally known as ReactiveCocoa). It allows you to write functional reactive code by providing a declarative Swift API. Functional Reactive Programming (FRP) languages allow you to process values over time. Examples of these kinds of values include network responses, user interface events, and other types of asynchronous data.

The approach i used here is not the only one you can always create a custom Publisher check this out

Just getting started with Combine? You might want to first take a look at Getting started with the Combine framework in Swift or my Combine Playground.

Also, the following WWDC sessions are a great start to give yourself some more background information:

And you can read more on Combine in my other blog posts:

Thanks!

--

--