Making business logic clear with Reactive Programming

Olivier Bazin
Cheerz Engineering
Published in
5 min readMay 27, 2019

At Cheerz, we wanted recently to write our custom GooglePlaces based component, to allow our customers to input their shipping address in a trustable way. Why custom? Because we needed to have more control on requests number, characters needed to perform a search, custom timeouts and so on. We naturally chose RxSwift/RxCocoa to help us dealing with search scenarios.

KISS: Keep It Simple and Stupid!

Let’s list our needs

Ask ourselves the question “What ingredients do we need to create this component?” Well, we want our new screen with:

  • A tableView
  • A searchBar at top of the screen
  • A way to wire the search performed by the user to a Google request
  • Display autocomplete results in our tableView
  • A way to wire the address selection performed by the user to an auto-dismiss of our screen, going back to our address form, filled by the selection

Business Rules: no search unless search text has at least 4 characters, and we don’t want, if the user uses his device very quickly, to launch a request for each character but only when a pause is made (for example 0.5 s of non-activity)

Traditional way of thinking

Seeing these bullet points, we might be tempted to use only the classical tools provided by Apple : UISearchBarDelegate, UITableViewDataSource, and UITableViewDelegate, essentially.

Problem with this approach: classical delegation methods don’t allow you to write some code which deals synthetically with different moments of your flow. Let’s take the example of the text search. You have to :

  • implement the delegate method textInputChange
  • In this method, you have to write a condition (like text.count > 3) and if this condition is met, call Google services
  • But wait! We said we want to group user inputs to do not call Google services too often. So you also have to implement a timer to regularly make calls if the input has changed. Why a timer? Because calculating the difference of time between each input isn’t sufficient: you will never know if the last char is really the last in the user’s intention

So basically, you need for this simple work, because the delegate method of the timer is another piece of code than the searchBar one, to write some flags, i.e “currentSearch”, “timeSinceLastSearch”, and so on. We all know that such flags are a real pain to maintain, and our component is likely to be very stateful and therefore not bugproof.

From flags to observers

We can now say in a very simple way what a flag is: setting a value representing the fact that something happened with or without a value attached to this thing. Later in the lifecycle of the application, we will, therefore, be able to write conditions implying several flags and make decisions (calling Google, waiting for a while …etc …). But because these values represent the past we must, of course, reset them when needed in order to always have a concrete representation of at state. Then, and only then, we can rely on them and react to changes which happened.

In other words, relying on flags is a simple but not so trustable way to connect events between them in order to write conditions and business rules.

But what if we could write these rules before these events even occurred? In our example, could be great if we could synchronize user input, time criteria, and minimum chars rule in a simple and declarative way, expressing our entire scenario in its simplicity?

Well, with Rx, we can write something like

searchText
.orEmpty
.distinctUntilChanged
.throttle(0.3)
.filter{ $0.count > 3}

Let’s see this chain in details

From separated properties to unified observable sequence

If we look at the rx-ification of the UISearchBar class (accessible from searchBar.rx), we can see we have a “text” property, which type is ControlProperty<String?>. A method can be called on it: asObservable(). This is exactly what we looked for: a direct wire between a UI Component and the code responsible to react to its changes.

To do so, the text property is really our starting point, and with Rx we will connect it to the work we need to perform in a very simple way:

  • First, we need to specify .orEmpty() in our Rx chain to be able to include text deletion in it.
  • Then, we can declare that we want a minimum separation between two firing events of 0.3 seconds with .throttle(0.3) : without any timer, we now have the guarantee that even if the user inputs his search very fast Rx will sequence it into reasonable time chunks.
A visual representation of what Throttle accomplishes
  • After that, we need .distinctUntilChanged to exclude consecutive identical search patterns
Ignoring a “2” because it immediately follows another one
  • Finally, we can specify our Rx chain to exclude patterns with a char length prior to 4 with filter{ $0.count > 3}

Conclusion: Observable streams as atomic business flows

Readability: the operation of connecting user search, Google calls, appearance/disappearance of results is now expressed in a very synthetic way. All our rules are now described in an observables+operators chain, and if we need to change something, all is in its place :-).

Self-documented code: the implementation of the search stream is so concise that a plain sentence to explain it should be longer than the code itself!

Even more importantly, we have now a natural-language-like expression of our business rules, so feedbacks between devs and other tech actors (POs, UXs for example) is way easier!!

Hope you’re now convinced that Rx can be easy and even simpler than some of the classical implementations we are used to manipulate.

To dive in the Rx’s sea :

Do you want to see more than what we’ve shown here?

Don’t hesitate to contact us, it would be a pleasure to talk, or even to welcome for you for a coffee at our office (France 🇫🇷-> Paris 🏙-> Place de Clichy / Saint Lazare 🚊)!

Just so you know, we are hiring engineers! Don’t hesitate to contact us, or check our page here!

--

--