Combine : Write your Swift code in reactive way
iOS application developers are on cloud nine ever since the introduction of functional programming using swift as it resolved a lot of convoluted problems which they had been encountering back then. Furthermore, apple has clearly sworn their allegiance to the iOS development fraternity with the introduction of reactive programming.
What is Reactive Programming?
Reactive programming is an asynchronous programming paradigm which is concerned with data streams and propagation of change. Reactive programming is mainly focused on asynchronous data streams. This can be considered as a potential instrument for defining the behaviour of an application in response to asynchronous events. Basically, it carries asynchronous operation handling code away from the delegate pattern that is very common in iOS, towards a more declarative form which makes asynchronous behaviour easier to reason about. However, reactive programming is not a new concept in iOS application development as developers have been using third party libraries like Reactive Cocoa,RxSwift for accomplishing Functional Reactive Programming in iOS.
What is Functional Reactive Programming (FRP)?
Functional Reactive Programming is a programming paradigm for reactive programming using the components of functional programming such as map, reduce, filter etc. To put it simply, it is a combination of Functional Programming as well as Reactive Programming. The complex operational sequences of functions will be hidden from outside world using higher order functions in Functional Reactive Programming as the latter would act as an intermediate layer.
What Combine is all about?
As per apple’s documentation, Combine is a framework, which was introduced during WWDC 2019, provides a declarative approach for describing how your app processes events. Rather than potentially implementing multiple delegate callbacks or completion handler closures, you can create a single processing chain for a given event source. Each part of the chain is a Combine operator that performs a distinct action on the elements received from the previous step.
Basically, it may not be correct to refer Combine as a typical example of Functional Reactive Framework. Instead, it would be more accurate to consider it as a reactive programming framework that uses functional programming techniques. With the help of combine as a reactive programming framework, the asynchronous programming becomes less complex in Swift. Reactive code is basically more concise and easier to understand as we describe what we would like to accomplish. That is the reason why it is also called as declarative programming.
Consider an app that needs to filter a table or collection view based on the contents of a text field. In AppKit, each keystroke in the text field produces a Notification
that you can subscribe to with Combine. After receiving the notification, you can use operators to change the content and timing of event delivery, and use the final result to update your app’s user interface.
The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. As per Apple’s documentation, 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. Publishers have operators to act on the values received from upstream publishers and republish them.
- 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. This puts your subscriber code in control of how fast it receives events from the publishers it’s connected to.
Without further ado, lets directly jump into a real case scenario with which the efficacy of Combine framework can easily be described.
Calorie calculator! Let’s build an app using Combine
This section is mainly intended for people who have basic knowledge about Swift programming language and other iOS app development concepts.
Here, we are planning to develop an application that calculates the amount of calories burned after doing a specific exercise for specific amount of time.This can be calculated using a formula,
Total Calories Burned = (Duration of Exercise * MET * 3.5 * Weight of the person) / 200,
where MET is the Metabolic Equivalent of a Task (MET) — measures how many times more energy an activity burns in comparison to sitting still for the same period of time.
To start with, let’s create a single view iOS app that uses Swift & Storyboard.
Create a ViewController in the storyboard with 3 UITextFields for providing weight of the person, duration of exercise in minutes, and the type of exercise and 1 UILabel for showing the total calories burned.
As you can see there are 3 UITextfields with placeholders ‘Event,Weight & Duration in minutes’. There is also a UILabel that is having a text ‘Result’. As the context of this articles is not about stylising the User Interface, let’s keep the UI efforts to a bare minimum level. Having said that, setting up of proper auto layout constraints, in this case, may be required.
As the very next step, let’s create 3 UITextfield & 1 UILabel properties in the ViewController.swift file which is the corresponding class of this above UIViewController in the storyboard and connect them with the UI elements.
Please make sure that Keyboard type under Text Input Traits of weight textfield and duration textfield are selected as ‘Decimal Pad’.
After that, we can create a UIPickerView constant and initialise it. This is for receiving eventTextField data as we are going to select the event from a picker view.
let eventPickerView = UIPickerView()
Make sure to properly set delegate and datasource of UIPickerView as self in the viewDidLoad function. After that let’s assign the inputView of eventTextField as eventPickerView.
What will be the data that can be shown in this eventPickerView?
For that, let’s create a struct called Event with two attributes.
1. title as a String
2.met as a Double
where title is for displaying the exercise name and met is for storing the MET value of that exercise.
After that, let’s create an Array of Events for feeding the Pickerview.
In addition to that, we need to create a Double variable for storing the selected MET value.
private var selectedMET : Double? = 0.0
It is imperative to implement the delegate and datasource methods of eventPickerView. So, in order to keep these from our actual business logics and as a standard way of implementation, let’s create an extension of our ViewController class and implement these methods in it.
As you can see, the numberOfRowsInComponent datasource method is provided with a value of total events and the titleForRow datasource method returns the corresponding event title. Once a row is selected, didSelectRow delegate method will be fired and the selectedMET will be assigned with the corresponding met value of the selected event and the eventTextField text will be updated with the title value.
As we discussed before, each value change in a textfield produces a notification which can be subscribed with Combine. So, let’s create a notification center property like this.
private let notificationCenter = NotificationCenter.default
and observe all key stroke changes that are happening in all these textfields so that whenever the data in these textfield changes we can receive those changes and do something.For that, let’s create a private function observeInputDataFields() for implementing this idea and call it in ViewDidLoad.
Here comes the Combine part. To start with, let’s import Combine module into our code by
import Combine
and implement some publishers for publishing changes that are happening in textfields and subscribers for receiving them in observeInputDataFields method.
Any changes in a textfield can be published by using publisher method of NotificationCenter and it accepts two parameters which are for and object respectively.
Here, in our case, publisher is for observing changes in text in UITextfields. So, lets pass UITextField.textDidChangeNotification as the first parameter(for:) and the textfield itself as the second parameter(object:). textDidChangeNotification is for making the publisher aware that it should publish the notification whenever a change happens in the corresponding textfield object. So the code will be like,
notificationCenter.publisher(for: UITextField.textDidChangeNotification, object: weightTextField)
notificationCenter.publisher(for: UITextField.textDidChangeNotification, object: durationTextField)
notificationCenter.publisher(for: UITextField.textDidChangeNotification, object: eventTextField)
Once this is done, we have to implement the subscribers for dealing with textfield text changes. These can be done by attaching a subscriber called sink which is one of the fundamental component of Combine. Basically , sink attaches a subscriber with closure-based behaviour to a publisher. It can be implemented in two ways.
1. A behaviour that fails, using an extra parameter called receiveCompletionfunc sink(receiveCompletion: ((Subscribers.Completion<Self.Failure>) -> Void), receiveValue: ((Self.Output) -> Void)) -> AnyCancellable
2.A behaviour that never fails func sink(receiveValue: ((Self.Output) -> Void)) -> AnyCancellable
Here, we can implement using a behaviour that never fails.
Here, in the above snippet you can see three warnings with a message “Result of call to ‘sink(receiveValue:) is unused”. It means, when attaching a new subscriber, a Combine publisher always returns an object that conforms to the Cancellable
protocol, which acts as a token for the new subscription. We then need to retain that token for as long as we want the subscription to remain active, since once it gets deallocated, our subscription will automatically get cancelled (In fact, this can be cancelled manually by calling cancel()
on the token). So for that we have to create
private var subscribers = Set<AnyCancellable>()
to which we can store all these tokens. So our current implementation would be like,
Now, let’s receive the textfield values through Publisher in each closures and assign them to corresponding variables. For that, we have to receive the corresponding textfields in these closures and extract data from them.
Where should we store these data from Publishers? For that we can create some optional variables as we normally do.
But, since these variables are observable objects that automatically announce when changes occur, we have to prepend them with ‘@Published’ which is one of the most useful property wrappers.
So, how is it possible to get the corresponding textfields in these closures?
These can be obtained by using ‘$0’ which is the first argument passed into in a closure.
So what exactly is $0 in our case? It is none other than the output of Publisher.
Once a Publisher provides some output, we have to check whether it is a valid one or not. For that we can implement a guard statement with which we can check whether the Publisher Output is a Textfield and that text is not nil and moreover it is not empty. So, once we get the data from Publishers we need to store them in some variables. In our case, the data provided by them should only be Double as the Weight,Duration and MET values should not be string or any other types. So, we have to add an extra condition that returns false if casting the data to Double failed.
Once all these conditions are met, we can assign data to the corresponding observable objects.
So the whole snippet will be like,
Next, we have to listen to these observable variables, so that when ever a change occurs in any of the observable variables, we have to calculate the calculate the total calories burned by applying the data in all these parameters to the formula which we mentioned at first. These can be done by using a publisher that receives and combines the latest elements from three publishers, which is ‘Publishers.CombineLatest3’. There are different types of publishers like this that receive and combine latest elements from 2,3 or 4 different publishers. As we have 3 parameters here, we can use ‘Publishers.CombineLatest3’.
Here as well, we need to check whether the publishers are holding relevant data which can be used for calculating desirable output. This can be implemented by using guard statement.
Here, we are accessing the result label property for displaying the output and it can only be done by prepending it with a ‘self’ as it is implemented in a closure. This will obviously create a retain cycle which would in turn, create a memory leak. So in order to avoid that we have make self as ‘weak’ and check whether ‘self’ is nil or not. So for that we have to create an extra guard statement on the top which assigns self to ‘this’ constant, if available or else return. So the implementation will be like,
Once this is completed, we can calculate the result by implementing the formula and update the result label with the result obtained. So the actual result can be calculated by the statement below.
the data we obtained will be a Double, so we have to change it to a String for showing it as a text in the result label. For that, let’s create an extension of Double for converting it into a String like,
and use it like,
We can feed this string data into an attributed string with some styles and feed it to the label. These statements are optional, as it is purely a programmer’s discretion.
In this project, I have created two attributed strings exclusively for showing the result as well as its unit. For that I created an extension of NSAttributedString which has a function called ‘formatText’ which accepts text and size as parameter and returns it with the style.
and use it like,
Finally, I updated the result label after combing these two attributed strings using NSMutableAttributedString.
So, whenever there is a change in any of the textfield or event picker view, the result will be updated immediately, thanks to publishers & subscribers which are implemented.
The most significant reason why the reactive programming in iOS is ineffable is that, a business logic can be implemented with a lot less code, less complexity and thereby less bugs. It is more concise and easier to understand because a programmer describes what he or she would like to accomplish. In addition to that, it makes asynchronous programming less complex and it manages less states. A reactive application observes and react to streams of data and it does not hold to a state. This would, in turn, helps the developers to understand and maintain them expeditiously.
Conclusion
In this article, we just discussed about Combine module in swift and Reactive & Functional Reactive Programming in a bird's-eye view. The project which we discussed above covers basic components of Combine and the ethereal beauty of it can be more visibly seen when we use it for initiating network calls. In order to make it easier to understand, I selected a basic use case and you can checkout the complete code from the GitHub link provided below.
If you found this article relevant and useful, please follow me on medium. Please feel free to ask if you have any questions under comment section.
Functional reactive programming (FRP) is a programming paradigm for reactive programming ( asynchronous dataflow programming) using the building blocks of functional programming (e.g. map, reduce, filter)