Navigation 2.0: Route to Native

Moving our codebase to native

DPG Media
Level Up Coding

--

Written by: Tijs Teulings, React Native Specialist

Some of DPG Media’s apps now feature very different navigation. We went native. This is a ‘behind the scenes’ story of how we got there, and what’s in store next!

Why do we need Navigation 2.0?

For the past few months, our tech teams have worked hard to deliver Navigation 2.0. When you head over to the App Store and download the AD, HLN or 7sur7 app, or any of our regional titles; ED, Tubantia, BN DeStem, De Stentor, PZC, Brabants Dagblad or De Gelderlander, you will get version 8 of our mobile app featuring this completely revamped navigation system.

Up until version 8, one of our apps’ unique features was its cube-like navigation system. You could not just scroll up and down or tap an item in a menu. But you could also swipe the current section (or world as we called it) to the left or right and see the current screen animate out and away like you were rotating a square news planet. This was different from any other news app you might have used in the past. It did present some challenges though. This effect was hard to fit into standard navigation patterns technically. It also left some users lost as they were transported from one world to another, unable to find their way back to what they were reading previously.

So in 2021, work started to design a new navigation system that would prevent our users from getting lost and make sure they could find all the destinations our app offered without fail. One of the topics in discussions and design sprints was the return to a more common navigation with a tab bar. And this is what made it into the final designs delivered in version 8 of the app.

React Native vs. Native

When I joined in 2018, the team had just started working on bringing mobile app development in-house and creating a new news app from scratch. The web team had done work to make a news world in React Native using familiar tooling: Javascript, and declarative UI development. Our team combined this news world with a Topics world and a Video world into the new cube framework. A framework that could be used not just by AD but by all news brands from the same code base.

At the time, React Native was about five years old and already getting very popular. It allowed teams to quickly iterate on components and re-use views and other code between iOS and Android with only minor tweaks. And we could do all of it while mainly writing JavaScript, and later TypeScript. So, for certain modules where it made sense, we went back to native languages. This worked quite well for a while and allowed us to launch all ten new news apps in the summer of that same year.

Over the past few years, as the reach of our apps continued to rise and as our team continued to grow, we did start to run into some limits of React Native. It made sense to use the native languages of the iOS and Android platforms for some of our new features. And as more of our codebase was written in Kotlin and Swift, we needed more communication between the TypeScript layers of the app and the Native modules. While this worked, it tended to introduce complexity and thus also potential for bugs.

Our team now had more developers experienced in native frameworks. The focus of the development had changed from getting that first version out of the door to improving the app’s stability. Overall, it started to make more sense to move more of the codebase to Native.

Go Native

The biggest source of bugs and performance problems in our app at the time was the navigation system. Each world in our app depended on a React Native navigation library. We saw the most significant potential for improvement in moving this JavaScript-based system to the default native navigation systems of iOS and Android. Since this library was integrated into each world and many components in these worlds depended on it, we could not just make a native navigation and then move all the existing screens to this new navigation.

First, we needed to sever all these dependencies and make the screens work as standalone components in a native environment and in a React Native environment. A project dubbed “Go Native”. We started shipping more and more native code with each new release of the app. But we were running into screens or components that just would not work in a backward-compatible way, or that relied on other teams that did not necessarily have the same priorities.

So at some point, with some screens moved to native but still no fully native navigation, the Go Native project got stuck as we could not move forward any further without breaking backward compatibility. Lucky for us, at this time, UX had started working on Navigation 2.0. One of the decisions was to move from worlds in a cube to sections in a tab bar — like many other mobile apps.

With the navigation now being redesigned and needing a complete overhaul, nothing was stopping us from moving from React navigation to a fully native system.

New Navigation, New Technology!

With this first blocker out of the way, we started rewriting the new navigation from scratch. Tab bars were built, ‘pill bars’ were created, and a ‘super menu’ was born. And since we were now a few years further, we were able to do it using the latest and greatest tools available. Both iOS and Android had recently introduced tools to create screens and components using the same declarative UI concepts we were already familiar with in React Native. So new iOS screens could now use SwiftUI, and for Android screens, we opted to use Jetpack Compose. Since I and the other React Native developers on our team were already familiar with the basic concepts of these frameworks (which were partly inspired by React Native) we could quickly get up to speed in this new stack.

So, this is how a new navigation design made it possible to adopt new technologies that allowed us to get unstuck in continuing to move toward a more native app.

Stability vs. Reusable Code

Since version 1 in 2018, we went from 90% JavaScript and TypeScript to about 50% in the current app. The other half is Swift on iOS and Kotlin on Android. But since new components and screens can now more easily be built using native technology, we expect this to change to a 100% native app gradually.

As a result, we lose some benefits of React Native. The biggest is cross-platform reusable code or “write once, use everywhere”. Eventually though, we feel that the improved stability provided by using the official tooling provided by Apple and Google for each platform outweighs this benefit. It now takes a little longer to create the same screen twice for both platforms. But we no longer have to write code to bridge the communication from native modules to JavaScript and back. Also, some of our Android and iOS developers are now happier, definitely a metric worth mentioning

What’s Next for Navigation 2.0?

Of course, there is also still room for improvement. One of the benefits I will personally miss a lot is hot reloading. The instant feedback of views that update as you work on them in React Native is still unparalleled. While both SwiftUI and Compose try to offer similar preview functionality, it turns out that it just doesn’t work yet in a big project such as ours. A huge improvement we are planning to work on next by modularizing our rather large project into smaller units that should allow preview features to start working as intended.

Overall though, we believe this is the right way forward for our project. So a few months from now, we hope to share in one of our demos that we have moved to a 100% native app. But in the meantime, we continue to improve the new navigation by improving the super menu, adding personalization options, and many more cool features.

If you or your team are interested in hearing more about the nitty-gritty details of how we managed this migration, feel free to get in touch and leave your comments here. We will happily answer your questions and share experiences.

--

--

We are the tech team behind the digital products of all DPG Media’s brands and internal apps!