Building Reactive Applications With Combine

What Is Reactive Programming

Building Reactive Applications With Combine
Download Your Free Copy of
The Missing Manual
for Swift Development

The Guide I Wish I Had When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy

Combine is sometimes referred to as a functional reactive programming framework, but that isn't correct. It is more accurate to describe Combine as a reactive programming framework that uses functional programming techniques. Don't worry if this is confusing. It isn't important that you understand why Combine isn't a functional reactive programming framework. What is important is that you understand what reactive programming is and how Combine leverages Swift's support for functional programming. This episode answers the question What is reactive programming? In the next episode, we explore Swift's support for functional programming.

Coming up with a definition for reactive programming is surprisingly challenging and some definitions make your head spin. I would like to start with a simple definition and show you what reactive programming is with an example. What is reactive programming?

Reactive programming is working with asynchronous streams of data.

There is more to reactive programming, but this definition is sufficient for now. I would like to break this definition up into two components, (1) asynchronous and (2) streams of data. These concepts are at the heart of reactive programming and Apple's Combine framework. It is essential that you understand these concepts before you start working with the Combine framework.

What Is Asynchronous Programming?

Many developers still get confused when they come across the word asynchronous. What does it mean? What is asynchronous programming? To understand what asynchronous programming is, we first need to understand its counterpart, synchronous programming. Let's use a playground to illustrate the difference.

Synchronous Programming

We add an import statement for the Foundation framework and define a URL for a remote resource. We use the URL to create a Data object and print the number of bytes in the Data object. The string FINISHED is printed to the console at the end of the playground.

import Foundation

let url = URL(string: "https://cdn.cocoacasts.com/7ba5c3e7df669703cd7f0f0d4cefa5e5947126a8/2.jpg")!

let data = try! Data(contentsOf: url)

print(data.count)

print("FINISHED")

Run the contents of the playground and inspect the output in the console. The number of bytes in the Data object is printed before the string FINISHED is printed. This isn't surprising since the statements of the playground are executed synchronously. The playground executes one statement at a time. The next statement is executed when the previous statement has finished executing. That is what makes synchronous programming easy to understand.

597628
FINISHED

Asynchronous Programming

Let's now take a look at asynchronous programming. We no longer create the Data object synchronously. We use Grand Central Dispatch, Apple's concurrency library, to create the Data object asynchronously.

import Foundation

let url = URL(string: "https://cdn.cocoacasts.com/7ba5c3e7df669703cd7f0f0d4cefa5e5947126a8/2.jpg")!

DispatchQueue.global().async {
    let data = try! Data(contentsOf: url)

    print(data.count)
}

print("FINISHED")

Run the contents of the playground. The output in the console illustrates the difference between synchronous and asynchronous programming. The print statement printing the string FINISHED precedes the print statement printing the number of bytes in the Data object.

FINISHED
597628

The statements of the playground are still executed synchronously with one exception. The closure that is passed to the async(_:) method of the global dispatch queue is executed asynchronously. This simply means that the closure is executed independently of the main playground flow.

Grand Central Dispatch submits the closure to a global dispatch queue and continues with the next statement in the playground. It doesn't wait for the statements in the closure to finish executing. At some point, the closure is executed and the print statement printing the number of bytes in the Data object is executed. That is why the print statement printing the string FINISHED comes first.

What Are Streams of Data?

To understand what streams of data are, I have created a simple application. The application shows a table view and a button in the top right. Every time the user taps the button, the view controller adds a row to the table view. Each row shows the date and time the user tapped the button.

What is reactive programming?

How does this example relate to reactive programming? Let's revisit the definition of reactive programming. Reactive programming is working with asynchronous streams of data.

Streams of data are the fundamental building blocks of a reactive application. A stream of data is nothing more than a sequence of events ordered in time. The button in the top right illustrates this. It generates a stream of tap events. Every time the user taps the button, an event, a tap in this example, is emitted. The view controller listens for these events and it responds by adding a row to its table view every time an event is emitted.

Let me explain this with a diagram. The horizontal line represents time. Each circle on the line represents an event, the user tapping the button in this example. The view controller listens for these events and responds every time an event is emitted. This representation is also known as a marble diagram. Marble diagrams are very useful to learn about reactive programming concepts. We use them throughout this series.

What is a stream of data?

What Is the Observer Pattern?

It is time for a bit of theory. The pattern that drives reactive programming is the observer pattern. You may be new to reactive programming, but the observer pattern should be familiar. Chances are that you have used the observer pattern in some, if not most, of your projects. Notifications are an example of the observer pattern and even delegation can be considered an example of the observer pattern.

The concept is easy to understand. It defines subjects and observers. The observer is interested in changes of the subject. Every time the subject changes, the observers of the subject are notified. That is the observer pattern in a nutshell.

Let's use notifications to illustrate the observer pattern. In the viewDidLoad() method of the ViewController class we invoke the addObserver(forName:object:queue:using:) method on the application's default notification center. The addObserver(forName:object:queue:using:) method accepts four arguments. We are interested in the second argument and the fourth argument. The second argument is the subject the view controller observes, the UIApplication singleton in this example. The fourth argument is the observer, a closure that is executed every time a notification is posted by the subject.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Observe Did Enter Background Notification
    NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: UIApplication.shared, queue: nil) { (_) in
        print("did enter background")
    }
}

The advantage of the observer pattern is that subjects and observers are loosely coupled. The subject and the observer don't have a strictly defined relationship. It is the responsibility of the observer to subscribe to changes of the subject. The observer unsubscribes when it is no longer interested in changes of the subject.

Combine Terminology

Before we move on, we need to become familiar with the terminology of reactive programming. The observer pattern defines subjects and observers. The Combine framework refers to publishers instead of subjects and subscribers instead of observers. The button generates a stream of data to which the view controller listens. The Combine framework uses the term subscribing instead of listening.

Publishers, subscribers, and subscribing are three keywords you need to remember. We use them throughout this series.

Asynchronous Streams of Data

Let's revisit the definition of reactive programming we started with. Reactive programming is working with asynchronous streams of data. With what we learned in this episode, we can rephrase this definition. Reactive programming is working with publishers that asynchronously publish events to which a subscriber can subscribe.

Why Should You Adopt Reactive Programming?

If you are new to reactive programming, then you may still be wondering why you should adopt reactive programming. You are interested, but you are not sure if reactive programming is for you. Truth be told, I wasn't convinced the first time I came across reactive programming.

One of the most compelling benefits is that reactive programming makes asynchronous programming less complex. Why that is becomes clear later in this series.

Reactive code is often more concise and easier to understand because you describe what you would like to accomplish. This is also known as declarative programming.

Another benefit I enjoy immensely is that reactive applications manage less state. A reactive application observes and reacts to streams of data. It doesn't hold onto state. This results in code that is easier to understand and maintain.

What's Next?

It takes time to let these concepts sink in. Don't worry if you are still a bit confused. At this point, it is important that you understand what reactive programming is and what it is about. Reactive programming can be challenging to learn if you skip the basics.

Download Your Free Copy of
The Missing Manual
for Swift Development

The Guide I Wish I Had When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy
Next Episode "First-Class Functions and Function Types"