Your Privacy

This site uses cookies to enhance your browsing experience and deliver personalized content. By continuing to use this site, you consent to our use of cookies.
COOKIE POLICY

Why ReactiveX Programming Will Improve Code Quality

Why ReactiveX Programming Will Improve Code Quality
Back to insights

What is reactive programming?

Reactive programming is about designing your program based on how inputs are received and turned into outputs. This thought process applies whether you’re talking about a user typing a search term in a textbox and you are displaying search results or a scheduled batch job to migrate data from one database to another, both have inputs and outputs.

We find polling for data incredibly annoying in real life, the kids in the back of the car continually asking “Are we there yet? Are we there yet? Are we there yet?” the answer is “No! I will tell you when we get there!”. This is the way we have become accustomed to handling certain types of interactions in our programs already. We do not constantly ask a button if it has been pressed, we give the button a place to tell us when it has been pressed.

You might be thinking isn’t this Event Driven design? Event driven design focuses on user-initiated events, but does not generally address data models being asynchronously changed and how those changes should be shared with a UI. Reactive programming events are triggered by change events both user driven and data driven. So reactive programming gives you a feedback loop for your UI to know when an underlying model has changed and your user should be updated.

Now let’s look at the definition: reactive programming is an asynchronous programming paradigm oriented around data streams and the propagation of change. So what does that actually mean? Well let’s break it down slowly, “asynchronous programming paradigm” so instead of having things happen in a sequence “I went for a walk and then I read a book”, you do them concurrently “I went for a walk and listened to an audio book at the same time”. Hopefully, this concept isn’t foreign to anyone at this stage. Now we get to the “Wut!?” portion: “oriented around data streams and the propagation of change”. What this portion of the definition is referring to is that a program is a dynamic thing that has input and output, if your program doesn’t have input then you might as well replace it with a constant, if it doesn’t have output then what’s the point of ever running it?

Designing around data streams and change propagation

One of the biggest challenges for people beginning to look at reactive programming is the required shift in mindset to thinking about streams of data. This thought process is actually something that most of us are already familiar with though because we have been dealing with callbacks. We don’t poll for changes anymore, we pass a callback function or delegate so that we can be updated when something happens and REACT to it.

“So why are you writing this intro like reactive programming is something different? You just said I’m already doing it.” The difference is in the interface, and for this part I will be referring to the implementation of Rx. Quick aside; I am a fan of Rx because there is a sizable community behind it and there is an implementation that is fairly uniform in most modern programming languages. This correlation between multiple languages actually helps developers from multiple teams discuss how to solve problems using a common API </aside>. Rx creates a generic interface for handling events, Observable streams. Let’s look at a swifty implementation:

Using target action


import UIKit

class ViewController: UIViewController {

let label: UILabel = UILabel(frame: CGRect(x: 20, y: 150, width: 100, height: 50))

var counter: Int = 0

override func viewDidLoad() {

super.viewDidLoad()

view = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 667))

let button: UIButton = UIButton(frame: CGRect(x: 20, y: 50, width: 100, height: 50))

button.setTitle("Press Me", for: .normal)

// Tell the button to call updateCount when the touchUpInside event fires

button.addTarget(self, action: #selector(ViewController.updateCount), for: .touchUpInside)

label.textColor = UIColor.white

view.addSubview(button)

view.addSubview(label)

}

func updateCount() {

counter += 1 // update the counter

label.text = "Count: \(counter)" // update the label

}

}

Using Rx


import UIKit

import RxSwift

import RxCocoa

class ViewController: UIViewController {

let disposeBag = DisposeBag()

override func viewDidLoad() {

super.viewDidLoad()

view = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 667))

let button: UIButton = UIButton(frame: CGRect(x: 20, y: 50, width: 100, height: 50))

button.setTitle("Press Me", for: .normal)

let label: UILabel = UILabel(frame: CGRect(x: 20, y: 150, width: 100, height: 50))

label.textColor = UIColor.white

button.rx.tap

// keep a running count

.scan(0){ (currentCount: Int, _) -> Int in

return currentCount + 1

// change the type of data in the stream from an Int to a String

}.map { (updatedCount: Int) -> String in

return "Count: \(updatedCount)"

}.subscribe(label.rx.text)

.addDisposableTo(disposeBag)

view.addSubview(button)

view.addSubview(label)

}

}

This is an example of two different implementations of a simple view that has a button and a label that counts the number of times the button has been pressed.

The first implementation adds a callback with addTarget(_:,action:,for:) and in the implementation of updateCount() we update the counter and display the new value in the label. Even though this is a small program you already have symptoms of larger problems that go along with traditional event based target callbacks. The culprits? The variables counter and label. We are forced to have instance level properties and counter is only ever used in one place! (Yes, static function variables are a thing but they don’t exist in Swift and the work around is distracting.) The other property, label, is technically used in two places. It is initialized and added to its superview in viewDidLoad(), the text is then set in updateCount(), however as we see in the Rx example you don’t need to separate these things.

Let’s look at the second implementation and try to translate it into English. The chain of calls that starts with button.rx.tap and ends with addDisposableTo(disposeBag) translates as so: Listen to the taps on this button (button.rx.tap), when the button is tapped start with the value of 0 and run the closure that takes the previous value and add 1 to it (scan(0){…}), pass the new value along and save the new value for the next tap, next transform in Int stream type returned from the previous value update into a String (map{…}), now register the label text as the listener to this stream (subscribe(labe.rx.text)) and finally add this stream to the bag of things to throw away when the view controller is dismissed (addDisposableTo(disposeBag)).

This example shows the ability to take an input stream and transform it into what is needed for an output stream. There is no need for instance level variables so there is less of a chance that a teammate will misunderstand the purpose of the variable and use it where they shouldn’t. The local reasoning is also improved because you have one chain of functions for the stream going from input to output.

Observable streams

Ok so observable streams are streams of events or a series of events if that’s easier to think about. There are three types of events that an observable stream can provide onNext, onCompleted(or onDone), and onError. This makes sense if you think about a series of events taking place. Say you are at an all you can eat wing restaurant there are really three events you care about:

onNext – They bring you another basket of wings to devour.

onCompleted – Sorry we aren’t able to serve wings anymore because we ran out.

onError – Everybody run! The kitchen is on fire!

All observable streams have the same three event types that you as the consumer might need to think about. You can stop consuming the events (you get full and can’t eat any more delicious wings) at any point but that does not change the possible outcomes of the observable.

The call to the subscribe function adds on Observer to the Observable stream. An observer is exactly what it sounds like, something that observes an observable stream. An observer knows what to do with the different event types for an observable stream. In the code above I set the observer to the label text, but I could just have easily used a set of closure’s for each event type if the situation required it.

So why should I care?

Reactive programming will push some of the state management you generally have to deal with down into a library that is heavily tested and shared. In the simple examples shared here the management of state was not addressed, but a follow-up blog will show this powerful feature of reactive frameworks. If you choose the Rx framework you will get the benefit mentioned above with being able to collaborate with colleagues who use different languages to help each other create the best solutions possible.

Reactive programming will hopefully expose you to some common operators of functional programming. Starting you down that road will have you asking many more questions about how you design your code.

Next time we will go over more about observable streams, operators for mutating and combining streams as well as a more concrete example that will show how reactive can simplify a code base.

Digging In

  • Digital Products

    Energy 2025 – Expansion of Fossil Fuels or Carbon Reduction?

    Now that the election is behind us, we have an opportunity to anticipate the possible effects on the energy industry under this new administration. What strategies will be impacted? What will remain the same? What opportunities can we take advantage of in 2025? This blog is meant to dig into these questions and provide some […]

  • Digital Products

    The Growing Importance of Digital Accessibility

    Embracing Digital Accessibility: A Moral and Business Imperative In today’s digital landscape, accessibility has become crucial for businesses and organizations. With increasing awareness and legal requirements, ensuring that digital products are accessible to all users, including those with disabilities, is not just a compliance issue but a moral imperative. At UDig, we champion ADA compliance […]

  • Digital Products

    Unlocking Business Potential: The Power of Custom Application Development

    Like any savvy business leader, you’re likely always on the lookout for tools to give your company a competitive edge. And in doing so, you’ve undoubtedly considered investing in custom application development. But the question is, how do you ensure that such a major investment in a custom web application development provides a strong return on […]

  • Digital Products

    Mastering Legacy Application Modernization: Strategies for Success

    The ironic truth of the business world is that change is the only constant. But this means that failing to keep pace with the competition and its technologies will only end with you falling behind. That’s where legacy application modernization enters the fold. When you modernize legacy applications, your team gains access to new features […]

  • Digital Products

    CTO Confessions Podcast

    In this episode of CTO Confessions, Rob Phillips, the Vice President of Software Engineering at UDig, digs into his journey from a passionate technologist in his youth to a seasoned leader in the tech industry. He shares valuable lessons on transitioning to senior leadership, the importance of understanding and articulating company problems, and the art of empowering teams for high performance.

  • Digital Products

    Navigating the Challenges of On Premise to Cloud Migration

    In today’s rapidly evolving technological landscape, the shift from on premise solutions to cloud-based infrastructure has become a pivotal transformation for organizations seeking to modernize their IT operations. This transition holds the promise of increased agility, cost savings, and enhanced scalability. However, it is not without its set of formidable challenges that organizations must navigate. […]