How the performance trade-off in my cross-platform web app went unnoticed by users.
So it all started when I came up with the idea for an app that helps parents get their kids ready for school.
I’m a dad with 3 kids and I was annoyed by the fact that at work I have dozens of powerful tools to organise and manage my team. But at home to get the kids ready for school, I have nothing. It was chaos. So I thought… why not build a todo list for kids? I could make the UX look like a game and even embed gamification design elements to help keep kids focussed and engaged.
So I made School Morning Routine. It turned out awesome (you can check it out here). Now the kids get ready for school in ~30% of the time with 95% less nagging. (Yes I measured ;)
But during my development, I made a huge mistake. I wasted a lot of time building a native iOS app.
Choosing a mobile app technology (aka pick your poison)
Now, the problem with starting a mobile app in 2022 is that there are a lot of totally different technical directions you can take: native, cross-platform web app, React Native, Flutter, Progressive Web App, Xamarin, etc.
The default way of making an app is to code it 3 times, once for iOS, once for Android, and once for the Web.
But, coding the same thing multiple times is deeply disturbing and unnatural to us software developers. So over the years, we have created dozens of approaches to try and “code once, run anywhere”. But they all involve nasty trade-offs.
Cross-Platform Web Apps
With cross-platform web apps, you write code once using common web technologies and deploy it to multiple platforms. With a little sprinkling of native code where you need to use iOS and Android features that aren’t available from a browser.
But the nasty trade-off is performance.
In 2014 I had actually tried to use the Ionic Framework to make a different app and I’d discovered what most people did: Android and iOS are terrible at running web apps.
They are slow, unpredictable, janky, flickery, stuttering and the touch interactions are flakey.
So I decided pretty early on that School Morning Routine couldn’t be a cross-platform web app. My app would involve heavy use of game-style animations and since it’s for kids, it would need to have excellent touch interactions.
So… I decided to make native apps. They are generally the least risky and highest quality. Of course, it sucks having to write the same app multiple times, but it’s a small app anyway… and I believe in elbow grease over magic.
(Speaking of my dislike of magic, I won’t address here the reasons that I didn’t choose one of the current tech darlings like Flutter or React Native. Those are interesting topics that deserve their own blog post sometime in the future…)
So, first I wrote a gorgeous iOS app. I iterated on it a bunch of times with beta testers. Then I released it on the App Store to some really positive feedback from initial users. 5-star reviews, gushing emails from users with changed lives, the works.
Excited that I’m on to something successful, I decided to build the web app next. I used React with CSS animations, framer motion, and a dash of delightful Lottie animations thrown in. When I finished I spent an afternoon carefully performance tuning. Just making sure that there were no unnecessary renders. Sigh… #react_life
My kids had been using the native iOS version of School Morning Routine for a few weeks by that point. So to test out the new cross-platform web app version, I put it on my kid’s iPad. They could test it by using it to be ready for school.
Funnily enough, I just plain forgot to tell them that I’d changed the app from native to a web app. But the morning came and went and they didn’t notice.
They didn’t notice.
They didn’t even notice.
If you don’t have kids you may not realise this, but kids complain about EVERYTHING. Seriously, everything. But not only didn’t they complain about using a web app, the next day I asked them if they noticed anything different. They actually thanked me because I’d had to use a different tick animation for the web version and 2/3 liked it better.
They were right, the animations were smooth as butter and the touch interactions were tight.
I was shocked. Ok, maybe it was just because I have a relatively new and high-powered iPad? So I went out and bought a low-end Android tablet. I picked one that was so underpowered that even opening the Android settings screen was janky and stuttered. But that’s an important test to run because for many people it’s the only device they have access to.
Well, I loaded up School Morning Routine, and guess what. It was… fine. Not brilliant, but hey, it’s a low-end Android tablet, what can you expect?
So… I went to my desk, deleted my native iOS app, and decided to use Ionic Capacitor.
Now I get to write one app and deploy it on three platforms. Check out my build scripts. With 3 commands I can deploy to an iOS app, an Android app, or deploy to my website on AWS!
How cool is that!
Since then I’ve released School Morning Routine on Android, iOS, and Web. Not only didn’t my iOS users notice, but I’ve also actually had fewer bugs. There was a troublesome bug caused by something to do with rendering a table view that only happened on iOS 14 and the stack trace was useless... sigh, #ios_life. But with my cross-platform web app that disappeared.
Somehow my cross-platform web app is actually more stable!
What is going on?
How is it possible to make an animation heavy app for kids using a web app?
It turns out that in 2022, for a lot of apps, the dream of write once run anywhere has finally arrived.
The cost/benefit tradeoff for cross-platform web apps has always been about trading worse performance in exchange for less development time. In 2014 for most apps, that was a bad tradeoff. But in the past 8 years, a lot has changed. Browser performance has steadily increased:
In 2022, the cost/benefit calculation has changed.
So where are we now?
I’ve always been a fan of the Ionic team. They started a company years ago as early advocates of cross-platform web apps. I love what they do, but I’ve always felt bad for them. It seemed like they bet on the wrong horse. The tech underpinning cross-platform web apps just couldn’t support their dream.
I think today, technology has finally caught up with the Ionic team’s vision.
School Morning Routine is the exact kind of app that a few years ago you would have been crazy to build as a cross-platform web app. But it works! It’s beautiful, seriously, check it out! I have the same app deployed to the Google Play store to the App Store and even available online.
It’s not just me either, late last year Josh Wardle created Wordle the mobile game that’s currently taking over the world. As I wrote in this post, it doesn’t even have a mobile app. It’s a progressive web app written using Web Components.
This is the post I wish I’d read before I started writing School Morning Routine. Because in the past I had disregarded cross platform web apps. I just assumed they were too slow. But they were the perfect choice for my app.
Every year browsers and web technologies become more capable and more powerful. Every year there are more kinds of app that you can make cross platform.
So before you start your next project, why don’t you take a look at cross platform web apps. Maybe they aren’t right for your project, but maybe, like me, you’ll discover that you can code once and run everywhere. And I think that’s amazing.
More content at PlainEnglish.io. Sign up for our free weekly newsletter. Follow us on Twitter and LinkedIn. Join our community Discord.
Author’s note: In a previous version of this post, I had speculated on the implications of the steady improvement of cross platform web apps. I stand by those wild speculations, but they were overshadowing the purpose of the post so I took them out.
I hope no-one considers this post a criticism of native development or native developers. I myself have written over a dozen native iOS and Android apps which I love and many of which are in production being used right now as you read this. Although cross platform web apps were the best choice for School Morning Routine, today, if I had to make an app that heavily used native features or was computationally demanding I’d still write a native app.