The Hybrid Approach

Most mobile apps today are written mostly in Swift, Flutter, or React Native. At Really Good Software we've taken a different approach. We use Webviews (the mobile equivalent to iFrames), served as html from a Ruby on Rails application, as the primary mechanism for building mobile app screens.

Benefits of a Webview Driven Approach

Benefit

Faster development speed

Faster feedback loops during testing (pull to refresh, open in browser)

Get a web app "for free"

Add extensive features without waiting for App Store Release

Fix bugs quickly without waiting for App Store Release

Drawbacks of a Webview Driven Approach

Issue
Solution?

Can have nasty bugs based on syncing logged-in-status

✅ Yes

Webviews can't do "Mobile Native" UX like stacks and modals

✅ Yes

Links and forms in webviews don't have "slide-over" transition effects

✅ Yes

Device-level features are harder to access

Somewhat

Users with slow connections have a somewhat degraded experience

❌ No

How Our Approach Works

  • We have a component called <WebViewStack /> which takes an initial url. Usually we have one per each tab in the app.

  • When a link is clicked, it is intercepted by onShouldStartLoadWithRequest and we check a few things.

    • Should we create a whole new Stack Context for the next page?

    • Should we execute any native functions?

  • Then, an entirely new webview is instantiated, with the next url, and depending on the parameters in the url, appears from the right, as a modal, or as a full screen modal.

Gotchas & Weird Stuff

A note on Top Navigation (The Header)

The "standard" way to do a top header on mobile is to use the one Apple or Android gives us. This means that there is a single Top Bar component that is used on every page, and it's behaviour changes depending on the screen the user is on. But in practical usage, we came across so many edge cases that our recommendation now is to ditch the Native Header, and build the header behaviour into the webviews themselves. This means we lose the transition behaviour where the back button appears on the left side.

Some of the challenges we had:

  • Conditionally showing & hiding the header - we can of course pass this from our webview to our header component, but we got into issues of timing (the header either appears too early or too late), and finding a nice UX here was very hard.

  • Defining consistent back navigation: When an app screen has a carousel in it - which we do often - navigating to the next slide doesn't trigger a navigation event in the router. This means the user can get several slides in, and when they hit the back button, it takes them back to outside the carousel, which from their perspective is several steps backward and bad UX. Of course we can wire this up so that the back button on the top bar is aware of the carousel steps, but the code starts to get super complex and difficult to read and update.

Last updated