I work on an app that has made a similar tradeoff. I won't restate the positives because the post describes many of them (after all, we chose a similar architecture) but I'll add that fetching app logic+assets (in the form of JS/HTML) from a server also lets us iterate on the app more quickly and without requiring the user to approve an update. (Edit: oops, he actually did mention this in the post!) And we get to use all our HTML-based testing tools (like automated screenshotting) to catch regressions.
Our app is also different in that we precache all the HTML in the background so we can always load it from disk rather than the network, which helps avoid any loading delays.
Here are some of the negatives I've encountered. (My experience is solely with Android but I believe the basics are also true on iPhone.)
- There's a long tail of details that are hard to get right. E.g., handling multiple device DPIs, or getting navigation right (Android has a confusing mess of back vs up and tasks vs activities). This is all solvable (after all, that's why you've gone hybrid), but it ends up pushing more of your code native than you'd originally expect.
- There's a delay on all click events on a webview. This is probably worthy of a longer blog post but to summarize, there are at least two causes of this.
One is that by default click events happen 300ms after the touch event (I think due to it waiting for a double-tap?). You can work around this by handling touch events specially (and there are JS libraries that claim to do it) but in practice it doesn't work in all cases (e.g. some form controls can't be worked around) and we ran into bugs on obscure devices (e.g. our users report they can't focus a form control on certain Samsung devices; we even got one of those devices but couldn't reproduce the problem). Alternatively, the newer Chrome-based webview turns off this delay if your content's meta tags indicate it's not zoomable, but you can't really rely on this because the Chrome-based webview isn't widely deployed.
The other source of lag is that the "active" DOM state is also delayed, but by some other number and unrelated subsystem. This means when you tap a button it'll light up a bit after you tap it. In my brief survey I think other mobile apps work around this by not having a prelight state.
Sorry to be full of bad news. (Like I wrote above, I still think this architecture is the right tradeoff for my application's purposes.)
The reality is that tap events won't work correctly until you actively fix them, and almost all of them can be fixed (including ghost clicks). Fastclick.js does help here a bit, but it's not perfect. We did a ton of work on our end to have a comprehensive tap solution (though some of it is specific to Ionic projects), but could be useful for inspiration for your own project: https://github.com/driftyco/ionic/blob/master/js/utils/tap.j...
Thanks for the link. We also have an in-house solution like fastclick.
The hard part about this is tracking down the long tail of failures, particularly when they affect devices you don't have access to like the Samsung phones I mentioned above. It means we cannot make any changes to the code we already have without risking breaking some users who currently work (and rely on our app; as separate from the users for whom the app has never worked).
I agree with the "long tail of details". The big wins are championed while seemingly infinite little gotchas are glossed over. Native development appears to be linear and straightforward because there is one language and one API - "one thing" to reason about. Contrast that to all the context switching involved in a hybrid app. Portions are written in Obj-C, portions are dynamically built using data, portions are written in JS, CSS, HTML, and Rails. What about additional tools like SASS, jQuery, JS MVC of choice, etc, etc? A lot of moving parts can be hard to juggle, and slow development as app complexity/entropy sets in.
> Native development appears to be linear and straightforward because there is one language and one API - "one thing" to reason about.
No. There are X languages and X API's to reason about when doing native development, X being the device OS's you want to target. If you're only targeting iOS and nothing else, then I would agree with you. This is a point that's also glossed over by native app proponents.
> If you're only targeting iOS and nothing else, then I would agree with you.
That's becoming outdated. We've got a native iOS app and we're doing the Android port with Apportable, which provides native iOS APIs and features on Android. The dev version (which we're using) implements its own Core Animation subsystem on top of OpenGL ES 2.
It's not perfect, but it's a hell of a lot better than maintaing two separate codebases and it's far easier to fix whatever platform issues there are than the equivalent "long tail of details" you encounter working with web views.
Cool, so that covers iOS and Android (with its own long tail I'm sure). You still have Web and Windows mobile to worry about assuming that platform continues to gain popularity.
Apportable could be ported to run on Windows Phone, there just hasn't been any interest (or so I'm told).
Much of it could also run via NaCl as a Chrome Packaged App, including the UI stuff. That's a much bigger change though, since you'd be supporting mouse events, not touch. But it's still doable.
Still takes longer for non trivial apps to get the it right on a large array of devices of X in Html than native. Especially using something like Xamarin I would gladly take up the challenge. Hybrid has it's place ofcourse; for more complex text markup embedded in apps.
Native feel is a false prophet. "Fitness for use" is what you should aim for. The web didn't have a native Windows 95 feel. ExtJS tried hard to achieve interfaces that aped the "native feel" of Windows. Only hardcore interface designers that actually know what "native feel" is care about native feel, and even then they will only care about it so long as the current fashion trend is achieving a native feel. Once non-native feeling apps achieve "fitness for use" en masses, the "native feel is canon" will no longer be part of the app zeitgeist.
Except that you can get a native feel, as Basecamp has clearly demonstrated, if you utilize native SDK's where needed. Did you even read the article or try out their apps?
Our app is also different in that we precache all the HTML in the background so we can always load it from disk rather than the network, which helps avoid any loading delays.
Here are some of the negatives I've encountered. (My experience is solely with Android but I believe the basics are also true on iPhone.)
- There's a long tail of details that are hard to get right. E.g., handling multiple device DPIs, or getting navigation right (Android has a confusing mess of back vs up and tasks vs activities). This is all solvable (after all, that's why you've gone hybrid), but it ends up pushing more of your code native than you'd originally expect.
- There's a delay on all click events on a webview. This is probably worthy of a longer blog post but to summarize, there are at least two causes of this.
One is that by default click events happen 300ms after the touch event (I think due to it waiting for a double-tap?). You can work around this by handling touch events specially (and there are JS libraries that claim to do it) but in practice it doesn't work in all cases (e.g. some form controls can't be worked around) and we ran into bugs on obscure devices (e.g. our users report they can't focus a form control on certain Samsung devices; we even got one of those devices but couldn't reproduce the problem). Alternatively, the newer Chrome-based webview turns off this delay if your content's meta tags indicate it's not zoomable, but you can't really rely on this because the Chrome-based webview isn't widely deployed.
The other source of lag is that the "active" DOM state is also delayed, but by some other number and unrelated subsystem. This means when you tap a button it'll light up a bit after you tap it. In my brief survey I think other mobile apps work around this by not having a prelight state.
Sorry to be full of bad news. (Like I wrote above, I still think this architecture is the right tradeoff for my application's purposes.)