Welcome to Building Reactive Applications With Combine. As the name suggests, this series zooms in on Apple's Combine framework. We cover the the ins and outs of the framework and you learn everything you need to know to make your projects reactive with the Combine framework.
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.
Swift isn't a functional programming language, but it does have a number of functional features. The Combine framework relies on these functional features so it is important that you understand the basics of functional programming. That is the focus of this and the next episode.
In the previous episode, you learned about function types and what it means for Swift to have first-class functions. With these concepts in mind, we continue exploring the functional features of the Swift programming language. In this episode, we explore pure functions and higher-order functions. We start with pure functions.
Earlier in this series, we defined reactive programming as working with asynchronous streams of data. Now that you know what asynchronous streams of data are, it is clear why asynchronous programming is such a powerful concept.
It is time to show you what Combine feels like and what it can do for you. What do you gain by using the Combine framework in your projects? Let's start with some good news. The Combine framework has a relatively small vocabulary. You don't need to spend days or weeks familiarizing yourself with a plethora of types, protocols, and terminology.
In the previous episode, you learned about publishers and subscribers. Combine creates a subscription when a subscriber is attached to a publisher. In the setupNotificationHandling() method of the RootViewModel class, the subscription is returned to the view model as an AnyCancellable instance. The view model holds on to the AnyCancellable instance to prevent the subscription from terminating early. We covered this in the previous episode.
We haven't simplified or improved the implementation of the setupNotificationHandling() method of the RootViewModel class by adding Combine to the mix. In fact, Combine introduced additional complexity and we now need to manage the subscriptions we create. It is true that Combine comes with a bit of overhead. The traditional Cocoa API seems to be the better choice. Right?
Streams of values are at the heart of reactive programming. These values are delivered by publishers. In this episode, we zoom in on the relationship between publishers and subscribers.
You learned in the previous episode that a publisher sends zero or more values. A publisher emits an error if something goes wrong and, if the publisher is finite, it can send a completion event. In this episode, we continue exploring the relationship between publishers and subscribers. We zoom in on the life cycle of a subscription.
The Cocoa frameworks use a range of asynchronous interfaces, including the target-action pattern, key-value observing, notifications, and callbacks. We can leverage the Combine framework to create a single, unified interface for asynchronous programming. This opens up a number of compelling advantages.
The Published property wrapper lowers the barrier to start integrating the Combine framework into a project. There are a few details you need to take into account, though. Remember from the previous episode that we need to address two issues. Let's look at the first issue.
We made good progress in the previous episodes, but we need to make some changes to the RootViewController and RootViewModel classes. We address three issues in this episode. First, the RootViewController class shouldn't be aware of CLLocation objects. Second, the RootViewModel class should expose a publisher that emits weather data. Third, we need to restore the pull-to-refresh feature. Let's get started.
The root view controller displays the weather data the publisher emits. Every time the publisher emits weather data, the child view controllers of the root view controller receive the weather data and display it to the user. This works fine, but we can make it more reactive.
In the previous episode, we put the foundation in place to reactify the DayViewController class. In this episode, you learn how to use publishers to drive the user interface of an application.
There are a few user interface issues we need to address in this episode. The day view controller no longer shows its activity indicator view while it is waiting for weather data and the user interface elements that show the weather data should only be shown when there is weather data to display. Let's find out how we can resolve these issues.
In this episode, we optimize the implementation of the day view controller and the day view model. There are a few details we need to take care of. First, the day view controller should display an error message when its view model emits an error. Second, the day view controller should only display its activity indicator view if it has no weather data to display. Let's tackle these problems one by one.
We ran into several issues in the previous episode. The assign(to:on) and sink(receiveValue:) methods are convenient, but they don't always cut it. We need a solution that is robust and scales with the complexity of the project.
The Combine framework defines a range of operators to combine publishers. Combining publishers is a common pattern in reactive programming. In this episode, we improve the efficiency of the RootViewModel class by combining multiple publishers using the zip and combineLatest operators.
In this and the next episode, we shift focus to the week view controller. Populating the week view requires a different approach. It contains a table view and the week view model manages an array of WeahterDayData objects. Even though the approach is different, the patterns we apply are similar.
In 2019, Apple introduced another powerful API alongside the Combine framework, diffable data sources. Diffable data sources make it almost trivial to build performant table and collection views. In this episode, I show you why diffable data sources work well with the Combine framework. I won't cover diffable data sources in detail in this series, but the API is straightforward and easy to pick up.
Earlier in this series, we broke the settings view. The root view controller acts as the delegate of the settings view controller, but nothing happens when the user updates a setting in the settings view. In this and the next episode, we fix what we broke and reactify the application's settings view.
In this episode, we fix the settings view using the Combine framework. We no longer rely on the delegation pattern to propagate settings changes. In the previous episode, we created a view model for the settings view controller. The settings view model exposes a publisher for each setting. Objects interested in settings changes can subscribe to these publishers. Let me show you how that works.
Earlier in this series, you learned that reactive programming can be defined as working with asynchronous streams of data. A network request is an asynchronous operation and that makes reactive programming an excellent match for networking.
Even though we successfully used the Combine framework to fetch data from the weather API, the implementation is incomplete. We ignored error handling up until now and it is time to take a closer look at errors and how to handle them.
Cloudy shows the user an error if it isn't able to fetch weather data from the weather API. We map any errors that are thrown to WeatherDataError in the RootViewModel class. This solution works fine and it is a fitting implementation for the weather application we are building. In this episode, I show you three other options to handle errors.
Infinite loops are every developer's nightmare, especially if they have disastrous consequences. You need to be mindful of infinite loops when working with Combine or any other reactive framework. The difficulty is that it isn't always obvious that you created an infinite loop. As a matter of fact, we introduced an infinite loop earlier in this series. You receive a bonus point if you can spot it.
Earlier in this series, you learned how a subject can bridge the gap between imperative programming and reactive programming. While subjects are convenient in many ways, they are not always the best option. The Combine framework provides another option. In this episode, we zoom in on futures and promises to bridge the gap between imperative programming and reactive programming.
In the previous episode, you learned about futures and promises. In this episode, I show you how to use them in a project.
The API the image service exposes no longer accepts a completion handler. It returns a publisher instead. This is an improvement, but the image service still uses completion handlers under the hood. In this episode, we replace the completion handlers the image service uses internally with publishers.