I recently released my first ever iPhone app: a game called Super Flipside. This is a re-imagining of an earlier game I created way back in 2010. I had always imagined it would work really well on iPhone, but I'm no iOS developer -- needing to sink a bunch of time into learning Objective-C or Swift was always the blocker for making it a reality. I had occasionally heard of alternative options like PhoneGap, which allows you write apps in standard HTML/CSS/Javascript and wrap them as native iOS and Android apps. As a web developer, this sounds fantastic; I could spend my time actually creating a game rather than learning new tech. In practice, however, every PhoneGap app I've ever used feels like garbage. They're slow and clunky and feel obviously non-native. But one day, I thought: maybe I should try it. Just to rule it out, you know?
Spoiler alert: I ended up creating Super Flipside as a PhoneGap app and it doesn't suck, but I'm still never going to do it again.
Performance
The very first thing I wanted to test with PhoneGap was performance. Could I feasibly create a game that ran at a solid 60 FPS with no input lag? I whipped up a new project with ImpactJS (a canvas-based HTML/Javascript game framework), added a couple of entities to the game world that would respond to touch, and built it for iOS using PhoneGap. To my surprise, it actually worked pretty well! The game ran at 50-60 FPS on my iPhone 5s, and there was no input lag to speak of. Very promising! So I added a few more entities to the world. Suddenly, it was running at around 30 FPS. Yikes.
30 FPS with only a few entities on screen is an immediate no-go. I looked at a few PhoneGap alternatives like Ejecta to no avail -- they did improve performance a bit but still did not yield the silky smooth 60 FPS I was looking for. Then I tried once last thing:
WKWebView
Under the hood, PhoneGap apps are just simple native apps that utilize webviews to display your content. Up until the release of iOS 8, the standard webview was UIWebView. The performance of this webview always paled in comparison to Safari's, as Safari was a unique snowflake that had some serious performance optimizations (like just-in-time compilation of Javascript) built in. In iOS8, Apple made these optimizations available to the average developer via WKWebView, a drop-in replacement for UIWebView. At the time, there was one Cordova plugin available that could build PhoneGap apps using a WKWebView, so I gave it a shot. Immediately, the difference was like night and day -- Super Flipside was running at a solid 60 FPS. Amazing! I threw even more entities at it and it held up just fine (or at least, it was good enough). It looked like PhoneGap was going to be viable option for developing a game after all. I was shocked, yet pleased with this discovery, and promptly began development.
Super Flipside's current performance
Despite this win, Javascript is still Javascript at the end of the day. Performance in the final game (with much more going on than in my early tests) is not quite as great as I would have hoped for. It does run perfectly on my brand new iPhone 7, but on my iPhone 5s it's just... not quite there. On the 5s, you can sometimes feel garbage collection happening. Background activity such as push notifications can slow everything down for a few seconds. It's just a little bit less than perfect, which can be enough to ruin the magic. I don't own any iPhone between the 5s and the 7, but I've played briefly on an iPhone 6 I would say it was 99% of the way there.
It was unplayable on my iPad 2. I didn't test on any other older devices, but I had a feeling their performance would be similarly terrible. I would rather people with old phones not even have the option to play than download it and be disappointed, so I limited Super Flipside to be available on 64-bit devices only. This was a little bit of a bummer but most iPhone users have upgraded past the 5c at this point anyway so it seemed like an okay tradeoff.
I did make some performance optimizations before I launched; many of them involved caching pre-rendered entities. For example, ImpactJS uses bitmap fonts for text. Each individual character drawn on screen is a separate draw call -- this is very slow. Since text is generally static content (although entire strings may be scaled, rotated, etc), I built some functionality in to cache entire strings once they've been rendered once. This particular optimization had an enormous effect. It cut the time it takes to render a frame of the level select screen nearly in half! I applied this technique to all kinds of "static" entities throughout the game and it yielded a noticeable improvement on the 5s.
However, the real take-away here is: if I had built the game natively, I don't think I would have had to worry about this kind of stuff at all. Super Flipside is a very visually simple 2D game. I know lower end Apple devices should be capable of running it perfectly; after all, they can run much higher end 3D games without a hitch. As far as I can tell, however, there's no way to harness the GPU when you go the PhoneGap route. While Super Flipside turned out alright, this is obviously a huge reason to just go native.
The Quirks
While sticking with Javascript was easier than learning Swift, using a webview means signing up for all kinds of strange quirks. For example, when you first load up the app, you may see a blank white screen for a split second as the page loads in the webview. The solution? Use the Cordova splashscreen plugin (which was, until a recent version, very buggy in and of itself) to artificially lengthen the time the launch image is displayed. This fix is doubly helpful as it was also a workaround for some weird behavior where sounds emitted by the webview don't respect the hardware mute switch until about half a second into the session. Blugh.
Speaking of audio, there's some alleged iOS behavior where audio won't play in a webview until the user interacts with it by tapping something. Sounds bad, right? There are ways to work around it but the terrifying part is: I couldn't trigger this limitation at all. Everything just worked like you would expect it to, contrary to what the Apple docs or anyone on StackOverflow said. So, I just kind of rolled with it, but this was very unnerving.
I hit quite a few quirks like these during development. The overall feeling I got here is that, when you develop webview-based apps, you are considered a second-class citizen. Using a webview to develop a full-fledged native game is kind of a hack, and I think Apple probably doesn't care if they break your app. Case in point: the iOS 10 beta introduced some new behavior where WebKit ignores the user-scalable viewport meta property. This "feature" is supposed to be for accessibility reasons; anyone should be able to pinch to zoom any web page. That's fine and all, but this would completely and totally break most webview-based apps. However, I again was unable to reproduce this behavior at all in Super Flipside. Maybe it's the way I'm handling touch events or something, but I just couldn't get it to break in the way it was supposed to. And again, I rolled with it, but felt very unsettled.
TLDR
I'm never going to develop a webview app again. Not having to learn a new language was great, but it wasn't worth the performance hit and quirks I encountered along the way. The fact that I should be able to easily port this to Android and other platforms is nice too, but I'm sure those platforms are going to have their own quirks and performance problems. If you're thinking about developing a game like this, just do yourself a favor and go native instead. You won't regret it.
Lesson learned, I guess.
Thanks for reading this post! Despite my complaints here, I am very happy with how Super Flipside turned out. It's a twitch action game in which you control two players at once, almost like a pacifist-mode bullet hell game. It's very difficult and meant to appeal to players who love trying a hard level again and again until they finally reach their "zen" zone and win. Sound cool to you? Why not download it for free from the App Store right now?