Getting Started With the Combine Framework in Swift

Photo by Jantine Doornbos on Unsplash

Combine is a new framework by Apple, introduced at WWDC 2019. The framework provides a declarative Swift API for processing values over time.

In other words, as described by Apple itself:

Customize handling of asynchronous events by combining event-processing operators.

At first, this might sound quite overwhelming. What does it actually mean? What can I do with it and when should I use it?

Hopefully, after reading this blog post, you should be able to answer these questions.


What is Combine?

The Combine framework can be compared to frameworks like RxSwift and ReactiveSwift (formally known as ReactiveCocoa).

It allows you to write function-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 Basic Principles of Combine

The basic principles of Combine make you understand how it works and how you can use it. Before we dive straight into the code examples, it’s better to start with some background information. This will help you to better understand how the code works and behaves.


Publishers and Subscribers

The Combine framework comes with so-called publishers and subscribers. If you’re familiar with RxSwift:

Different names, but they both give us the same understanding.

A publisher exposes values that can change, on which a subscriber subscribes to receive all those updates. Keep this in mind while we go over some examples of publishers available in the foundation framework while working with Combine.


The Foundation Framework and Combine

The foundation framework contains a lot of extensions for working with Combine. It allows you to work with common types you’re already familiar with.

Examples include:

Taking the last example, we can explain the concept of a publisher and a subscriber.

In the following code example, we create a new Publisher for our new blog post notification.

This publisher will listen for incoming notifications for the newBlogPost notification name. However, this will only happen once there is a subscriber.

We could, for example, create a lastPostTitleLabel which assigns subscribers to the publisher.

let lastPostLabel = UILabel()
let lastPostLabelSubscriber = Subscribers.Assign(object: lastPostLabel, keyPath: \.text)
blogPostPublisher.subscribe(lastPostLabelSubscriber)

If you try this code, you might notice that this doesn’t work yet.

It results in the following error:

Instance method ‘subscribe’ requires the types ‘NotificationCenter.Publisher.Output’ (aka ‘Notification’) and ‘String?’ be equivalent

The text property of the label needs to receive a String? value while the stream publishes a Notification.

Therefore, we need to use an operator you might be familiar with already: map. Using this operator we can change the output value from a Notification to the required String? type.

let blogPostPublisher = NotificationCenter.Publisher(center: .default, name: .newBlogPost, object: nil)
.map { (notification) -> String? in
return (notification.object as? BlogPost)?.title ?? ""
}

This will result in the following complete code example:

Whenever a new blog post is Published, the label Subscriber will update its text value. Great!


The Rules of a Subscription

Now that you’ve seen a basic example of a publisher and a subscriber in Combine, it’s time to go over the rules that come with a subscription:

That’s right, subscriptions can come with completion, but not always. Our Notification example is such a publisher which will never complete.

An example of a completing publisher is the URLSessionTask publisher, which will complete either with the data response or the request error.

Fact is that, whenever an error is thrown on a stream, the subscription is dismissed. Even if the stream allows multiple values to pass through.


@Published Usage to Bind Values to Changes

Now that we know the basics, we can jump into the @Published keyword.

This keyword is a property wrapper and adds a publisher to any property. A simple example can be a boolean which we assign to the enabled state of a UIButton:

To break this down:

This first thing you might notice is the dollar sign in front of isSubmitAllowed. It allows you to access the wrapped Publisher value. From that, you can access all the operators, or, as we did in the example, subscribe to it.


Memory Management in Combine

RxSwift comes with a DisposeBag and Combine comes with a AnyCancellable.

This class calls cancel() on deinit and makes sure subscriptions terminate early. Without implementing this you can end up with retain cycles.

Taking the above example, we can add it as follows to make sure our submit button subscription is released correctly:

The lifecycle of the switchSubscriber is linked to the lifecycle of the FormViewController.

Whenever the view controller is released, the property is released as well and the cancel() method is called.


Error Types and Streams

As soon as you start working with Combine, you’ll run into errors about mismatching error types.

Every publisher describes how they can fail and which error type can be expected. Just like we used the map operator in our notification example, you can use operators to recover or react from errors.

Common operators you might want to try out:


Debugging Combine Streams

Debugging functional reactive languages can be hard. It often results in long error descriptions and unreadable stack traces in Xcode.

long error descriptions

Often, this is a reason for developers to not use frameworks like RxSwift and ReactiveSwift. Looking at Combine, it seems that the experience isn’t any different.

Fortunately, there are ways to debug in Combine using the following operators:


A List of All Publisher Operators

Unfortunately, it’s hard to list all publisher operators here and keep them up to date. The best way to find them is by diving into the documentation topics.

However, to give you some idea, here’s a word web from WWDC:

Combine Operators in Swift
Combine Operators in Swift
Combine operators in Swift

Using Combine With MVVM

The Combine framework is perfectly suitable to work in combination with MVVM. In fact, it’s a lot better with Combine!

I’m not going into too much depth, but the example from before can be converted into an MVVM example as follows:


When Should I Use Combine?

Now that you know the basic principles of Combine, there’s an important question left: “When should you use it?”

If we take a look at a quote from the Apple docs, Combine gives you the following:

By adopting Combine, you’ll make your code easier to read and maintain, by centralizing your event-processing code and eliminating troublesome techniques like nested closures and convention-based callbacks.

Although this is definitely true, we just saw that debugging can be quite hard.

Another downside is that it comes with a learning curve. Not only you, but all your colleagues need to get into Combine and know how to work with it.

It’s not uncommon to end up with a project full of streams and subscribers, ending up with quite a difficult codebase if you’re not familiar with a framework like Combine.

Therefore, before you start using Combine, make sure to try it out in a somewhat smaller project.

Discuss the framework with your colleagues and ask yourself whether you require Combine for your code. If you do go for it, great!

Make sure, however, that you stay sharp and don’t end up with a project full of wires and hard to debug code, because that could eventually slow you down.

Go for composition first and benefit from Combine if state changes a lot and you require a lot of asynchronous code.


Conclusion

You should be able to get yourself started with Combine. The basic principles are explained, but there’s a lot more to cover.

A lot of the topics above could have a blog post of their own. Therefore, I encourage you to start exploring Combine yourself!

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

Thanks for reading!

Better Programming

Advice for programmers.

Antoine van der lee 🇳🇱

Written by

iOS Developer @WeTransfer — Follow me on twitter.com/twannl for more tips & tricks — Blogging weekly at https://avanderlee.com — Clap & hold for a surprise!

Better Programming

Advice for programmers.

More From Medium

More from Better Programming

More from Better Programming

More from Better Programming

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade