Last month we announced the availability of the HeroMode web app. This was of course a very exiting moment for our extremely tiny team. And, more importantly, I was finally able to use HeroMode both on my phone and on my laptop. My HeroMode-based productivity workflow was finally complete!
However, it actually took us longer than we had (wishfully) planned. In fact, we were (rather unreasonably) aiming for the web release in October 2022. By this standard, the eventual Jan. 2023 release was two months late. So what happened?
Here are some of the roadblocks we encountered. I might have already forgotten some of them, because selective memory is a great coping mechanism!
First of all, I should note that every dev team's experience with cross platform development is going to be different. There are a lot of factors involved. Like team size, budget, experience, preferred tech stack, time frame, product requirements, etc.
So to set the stage, here are our parameters:
- Single-digit dev team. The emphasis is on "single".
- Our budget is "stay as close to zero as possible".
- We are using React Native, Expo, and Firebase.
- HeroMode is our first cross platform app.
In addition, for HeroMode desktop, we decided to go with a web app instead of a standalone desktop app (say, with Electron, which I've been itching to try). The web choice was purely driven by user preference. For example, I personally always have many browser tabs open as I go about my day. Whereas I'm always hesitant to download a new app to my computer.
React Native for Web IS Amazing
Even though I'm going to talk about some challenges we faced, I still want to start off by saying how amazing the React Native ecosystem is. It's mind-blowing that we can write a single codebase that works on iOS and web. And, with Expo, a lot of the tedious setup is taken care of. It really makes our development experience much more enjoyable.
Some, Perhaps A Lot Of, Assembly Required
However, unless you're building a very simple app, you shouldn't expect it to automagically be mobile- and web-compatible out of the box. Even though HeroMode isn't a super complex app, it still took us a while to get it to work on the web.
One of the first issues we encountered is the native alert on mobile. It's very
simple to follow React Native documentation to implement alert dialogues, and HeroMode codebase is sprinkled with
However, it does not work on the web.
We decided to implement our custom alert dialog (built with React Native Paper),
and rip out all the
alert calls. This was a pretty tedious and painful pull
request, but it was worth it to have the same alert experience on both mobile
Above: Alert modal before (left) and after (right) implementation of custom dialog.
Authentication and Firebase
This is where we started to have web and native codes split into
module.native.js (and in the future, as we extend to Android support,
module.native.js might futher be split into
We use Google's Firebase SDK for web, and Invertase's React Native Firebase for mobile. And Firebase v8 to v9 have a lot of syntax changes. A lot. From import statements, to every CRUD operation. We had to write a lot of wrapper functions to abstract away the differences.
Once you go
module.native.js, there are many other modules
that would also benefit from such "mini split" treatment. Everything that doesn't
have full cross platform support, or has a different API on web and native, would
get their own
For example, Datetime Picker. When I first used Expo's Datetime Picker (on iOS), I was really excited by the result. It's super simple to use, and it looks great and consistent with general iOS experience.
However, Datetime Picker is not available on the web. So we had to add a web version (React).
Above: Mobile (left) vs Web (right) versions of date picker. The web version here is being run as a Chrome extension.
From there, we added more platform-specific UI components. Some are necessary because there's gap in cross platform support, but some are just for aesthetic to have components that look and interact joyfully with user input.
View Port and Scroll View
When, finally, we were able to run HeroMode on the web for the first time, we had much anticipation. Unfortunately, everything looked ugly and weird. All the proportions were bad, and we couldn't even scroll!
Above: Very ugly UI of the first "successful" build of HeroMode web.
Above: Another example of messed up UI while we were iterating on HeroMode web.
Unlike mobile devices, which have fixed height and width, web browsers can have different view port sizes. In addition, the HeroMode layout was built with mobile in mind, so it's very tall and narrow. On the other hand, typical web layout is wider. So we did the most frugal thing we could think of: we added side panels with background patterns to keep a concentrated, narrow layout in the middle of the page.
Above: A simple web layout for HeroMode.
Moreover, with the HeroMode Chrome extension, the nearly identical mobile experience is now available within any browser tab!
Above: Using HeroMode as a Chrome extension. You can open HeroMode within any browser tab.
But I got ahead of myself. Before we had the web version running, and before we set up the Chrome extension, we still had a lot more UI issues to tackle.
One major area is gesture handling. For example, tapping, dragging, and swiping of quest cards. On mobile, these are common and intuitive ways to interact with the app. And in HeroMode, that's how you can organize and prioritize quests, as well as delete or complete them.
On the web, however, the detected gesture is often different. Mouse click motion is quite different from a touch screen. And as a result, we had to implement slightly diverged gesture handlers for web and mobile.
HeroMode needs a payment system. On iOS, we use Revenue Cat to handle the in-app subscription purchase, which was a lot easier than doing the receipt handling on our own.
On the Web, however, we decided to go with Stripe as Revenue Cat's support on web is less extensive. So this is another part of the codebase that diverged into platform-specifc modules.
It's an Amazing Time to Build
Even though I've listed a lot of challenges we faced to build HeroMode on web, there are probably so many things that just automagically worked, to a point that I might've erroneously taken them for granted.
For example, navigation and routing. We use React Navigation for both web and mobile, and it just works. I don't remember having to do anything special to make it web-ready.
React Native Paper also mostly worked right out of the box when it comes to web compatibility. There were a few minor issues, but it was pretty simple to address by adding MUI in a platform-specific way.
React Native Calendar is another
component that worked pretty well on web. We had to make some adjustments, but
they were minor enough that we just sprinkled some
Platform.OS === "web" logics
instead of splitting into platform-specific modules.
And while we had to add web support to a lot of user interface components, many of them also simply just worked. For example, swipeable quest cards to show the delete and complete buttons. The card animations also mostly just worked.
Overall, it was really quite an adventure. There were many challenges, but it's amazing the amount of resources available. While it took us longer than we originally anticipated, mostly due to our estimate being bad, I've been a super happy, cross-platform user of HeroMode. And I hope you will be too!