What we noticed is that we’d incur substantial performance bottleneck for anything that needs to move data to/from native because of the number of encode/decode steps and “bridge hops”. For example to read a row from SQLite, count the bridge hops:
Webview JS -> Java: postMessage to read a row from SQLite
Java -> React Native JS: hey, a postMessage happened, what to do?
React Native JS -> Java: please select * from … where …
Java -> C: ok really run this SQL
Then the stack of conversions repeats on the way back to the webview:
SQL C -> Java -> RN JavaScript -> Java -> Webview JavaScript
The other thing that plagued us performance wise was boot-up speed. At the time (before Hermes JS VM for React Native), we’d have to wait for RN’s JS to boot, figure out our cache status, then boot the webview JS. And then the webview JS would do the bridge dance above to pull data from SQLite to render. Slow - 40 seconds on low end Android slow.
Today we are still mostly a web app, but our wrapper is pure native code. We cold start to a native view and boot the web app in the background. Our throughout & latency to native APIs is substantially faster without the extra bridge hops into and out of the RN JavaScript VM. We managed the original architecture swap from RN -> true native wrapper with a team of three - myself, our first iOS engineer, and our first Android engineer. We do have a large mobile team now though.
There’s some more FAQs and answers about this on Twitter here: https://twitter.com/jitl/status/1530326516013342723?s=46&t=x...
One thing I’d add is that deciding to use a webview wrapper is quite common for multiplatform rich text content editors. Google Docs, Dropbox Paper, Quip, Coda, Notion all use this architecture on iOS and Android because implementing an editor is extremely complex. It’s much more expensive to implement an editor 3x than say implementing a few list views and a form 3x.