iOS: How to implement a dynamic position of TabBar handled by RxFlow

Ngo Minh Tri
NE Digital
Published in
5 min readDec 28, 2020

--

Introduction

As an iOS developer, I believe that you once implemented TabBar, including UITabBarController & UITabBarItem which usually appears at the bottom of an app screen and provides the ability to switch between different sections quickly.

There are many ways to navigate between each tab: UIStoryboard with Segue or do it programmatically. My team is using RxFlow as a navigation framework for iOS applications. For those who haven’t hear about it, you can check out the link below:

RxFlow Part 1: In Theory

I will tell you about the refactoring process to handle the current static TabBar position into a dynamic TabBar position. The requirements come separately from Product Manager (PM), so I will split the story into three parts.

  • Part 1: Describe the legacy code that implements static TabBar position.
  • Part 2: Add one more tab.
  • Part 3: Make TabBar position dynamic by control via Remote Config.

The story

Part 1: Describe the legacy code that implements static TabBar position.

When I first come and touch on this piece of code, previous developers already apply RxFlow to implement navigation between three tabs: Profile, Payment, Address.

Notes: I try to illustrate the story and simplify the code by creating a demo project.

As you see, there are three folders: Address, Payment, Profile which contain Flow, Step, and ViewController files. AccountViewController is an UITabBarViewController. The main logic that controls TabBar position is in AccountFlow.

Above is a private function of AccountFlow, so what it does?

  • 1: Initial children flow.
  • 2: Trigger block executable when three flows are ready.
  • 3: Create tab bar items with titles and images.
  • 4: Set view controllers to UITabBarController.
  • 5: Return a whole FlowContributors with presentable and stepper instance.

Then, we use openWithoutDigitalClub(). Below is the overview how the code looks like.

A screen shot of result

Part 2: Add one more tab

PM: I want to add one more Digital Club tab at 4th position. It only appears when users are membership, otherwise hide it and keep three tabs.

Me: How does the application know that the user is membership?

PM: Basically, back-end service responds a boolean variable isMembership what controls the TabBar show/hide the 4th Digital Club tab.

Me: Hmm… not so difficult. It will take me 6 hours.

This is what I did:

1️⃣ Add one more case in AccountStep enum

2️⃣ I cloned whole method openWithoutDigitalClub() , rename it to openWithDigitalClub() , add 1 more tab at 4th position.

3️⃣ Then, I use openWithDigitalClub() in navigate(to step: Step)

❓Question: How to switch between steps?

From AccountViewController, I conform Stepper protocol. It provides a PublishRelay what I can update a specific step based on non-membership or membership users’ logic.

Above, I decided to open .digitalClub as an initial step. In case membership or non-membership is a response from your API service, you definitely can do as below:

The result with 4 tabs

Done, it is pretty easy, but do you recognise a duplicate code 🤔. Hmmm… let’s wait till the end. I’ll refactor it.

Part 3: Make TabBar position dynamic by control via Remote Config.

Another requirement came in.

PM: I want to move theDigital Club tab to the 2nd position.

Me: Yes, it’s easy. I only update the position of the item.

PM: So next time, if I want to move Digital Club back to the 4th position, you need to update and release another version to App Store?

Me: Of course, sir!

PM: Hmmm… could you please make it configurable?

Me: What do you mean configurable?

PM: Make the position of the four tabs as remote config, then in future, whenever I want to change positions, I go and update myself.

Me: Oh man, it takes much more effort than only move Digital Club to 2nd position. Let me think!

After a few brainstorming minutes, I remember what I learned from Data Structure & Algorithm subject. Is it possible to do a Key-Value mapping instead of using an array for TabBar? I ask myself. Eureka, I got a solution 🙌.

I decide to move to Dictionary (a Key-Value data structure in Swift, as similar as HashMap in Java). Let do it!

1️⃣ I create a TabKey enum to declare all cases of tab items.

And, I create a TabValue struct to store properties: UITabBarItem, Flow and Stepper.

2️⃣ I write a method that prepare a dictionary [TabKey : TabValue]

  • 1: Create all UITabBarItem with title and image
  • 2: Create all tab’s flow
  • 3: Create a dictionary of [TabKey : TabValue]

3️⃣ I create TabOutput struct to store [UITabBarItem], [Flow], [FlowContributor]

4️⃣ I will do a lookup based on TabKey and get according TabValue. Then I do a mapping from TabValue to TabOutput.

We will create another method with input is an array of TabKey what can be a response of API service or a Remote Config.

From here, the position of TabBar items will be reflected in the index of TabKey’s array.

Conclusion

There are many approaches to complete the task. I just want to share my own story of how I begin, deal with the requirements and make the code better at the end. Feel free to share your thought.

Thanks Li Hao for inspired me. Thanks Steve Dao for your advices.

Thanks all for reading until here. Happy coding and stay safe 🍀

--

--