How we designed a component leading to better readability, scaling, and testing

Solving View to Presenter communication on Android, for GoTransit.

How we designed a component leading to better readability, scaling, and testing

By Abhishek Jangra

GoTransit is our product which makes multimodal trips effortless for our customers. A quick read about GoTransit, here.

This blog will take you through an architecture component we designed while building GoTransit on Android. This component enables us to decouple the view from the presenter, with added benefits of better readability, scaling and testing.

Different screens in the GoTransit app

Beauty in design AND code

To ensure our code is as beautiful as our design, one of the pillars we set up for our architecture was to automate redundant technical setup whenever a developer was creating a new screen in the app, which was intended to save time. So, a good architecture is a long term (and constantly evolving) investment which makes product developers’ life better so they can focus on making customers’ life better.

Automating solutions to boring problems becomes a very interesting problem to solve.

While working on the architecture of the bigger Transport product, we decided to treat the changes on view as events. All the clicks, animations are modelled as events and we expose a stream (internally an RxJava PublishSubject) of these events which can be listened to by the presenter to know about the changes happening in the view.

Events for DepartureTime View
Screenshot for DepartureTime View

As you can see in the image above, the events are stored as a sealed class. Based on the listeners set, if the user makes an interaction, instead of worrying about how to supply this to the presenter, view will pass the corresponding event in a stream. This way the view becomes completely independent.

Another cool thing: Due to the sealed class of events, one could look at all the possible user interaction at a glance, instead of looking at the code and trying to find click listeners, scroll listeners etc., which is very painful.

But there were some problems…

Problem 1

Different flows triggered as user clicked 2 buttons quickly

In the video above, you can see the issue which has been bugging us since the dawn of humanity. The user could click multiple buttons and then two completely different things start happening on the screen. 🤦‍♂

Problem 2

Setup needed to use view events

As you can see in the screenshot above, every time a view is created, a stream has to be setup before defining the events. After that, each time there is an event, you need to do stream.onNext(event). It’s just not something which is very readable and easy to work with, especially when there’s no context on the codebase 😓

Let’s tackle Problem 1 first as that directly stops us from scaling the UX.

One solution used in a bunch of places is debouncing, where clicklisteners only trigger once in a set time frame.

If a user goes hulk mode on one button, it’ll still trigger the click listener only once.

But a user can still quickly press two different buttons placed side by side and trigger chaos. We needed something better, and this led us from debouncing to throttling. Treating click events as a stream of user interactions where only one is allowed in a time frame.

And we solved it. 🖖

We created a child sealed class for click events and moved some of the events there. Set up a new stream, everything coming is this stream is throlled by a defined time window (~500ms) and then pushed into the main view events stream. Now ONLY 1 click event will be addressed at a time. Other events like scrolling which need instant response can be sent directly into the view events stream.

But this wasn’t a moment to celebrate. 🙅‍♂
Remember Problem 2? While solving Problem 1, we added fuel to the fire and made the situation even worse.

From the presenter side, it is still cool as it just needs to subscribe to one stream of events. But on the view:

  1. Every time a developer creates a screen, they need to setup both the view events and click events stream with throttling
  2. Also, one needs to remember to send the event to click events stream if it’s a click event
And now, we embarked on the journey to solve Problem 2

We knew the basic idea. We needed some kind of an interface which will give any View the abilities to setup view events automatically.

So we created a new component ViewEventsDistributor and it works based on kotlin delegates.

I’ll cut to the chase and show you the code directly 👇

But this is the real treat, the new way for a view to setup view events

Yep, that’s about it! 🙌

The View will get the viewEvents() method which the presenter can use to subscribe to the events.

And how do you send an event? using sendViewEvent and sendClickEvent!

This is so much better and readable, someone new could easily understand it and jump right into the source to know more about it.

The developer working on a view does not need to worry about the setup, and could instead focus on making the customer’s life better.

And this is how we solved the riddle of view to presenter communication in GoTransit. Testing the presenter also becomes super easy. Just provide a mock view and fire event from the view to test how the presenter behaves.

If you’re planning to use some of this in your codebase, here’s a virtual high five 🖐

Click here to read more blogs from our vault.

Also, we’re hiring! Click below to view open job positions.