App Functionality Overview
WorryTime was developed for ReachOut.com to help sufferers of chronic worrying. The app allows users to enter worries, which are ‘locked’ away until the user’s scheduled worry time. At WorryTime the user can review their worries and take actions or dismiss worries that no longer concern them.
The first time we looked into Air for mobile was in 2012. At that time it was pretty sluggish and missing key features. A number of versions later, how does it perform now? Is it a viable alternative to native app development? Here are some the discoveries we made.
The killer feature. Apart from some icons and a different app.xml you can have a single codebase for Android and iOS, so this is a big plus.
We used FDT with Air SDK 16. Live debugging from a device worked well on iOS. On Android, publishing to the device directly from FDT is a bit temperamental. Deploying to a device is quite slow, so we also used the AIR Debug Launcher (ADL) for quicker feedback during development. Unfortunately ADL cannot simulate multitouch gestures so to properly test these features we had to continually deploy to a device.
Air’s performance has improved since we first looked into it. However, the recent move towards large, ultra high-res screens brings challenges for achieving good frame rates with the Air runtime. The full screen stage size on iPhone6+ is 1242 x 2208, and on a Samsung S5 is 1440 x 2560. These resolutions are bigger than most desktop monitors – we are not going to get a smooth 60FPS if we animate vector content at these sizes.
Vectors are great for handling multiple resolutions, but they can severely affect frame rates on mobile devices.
To retain flexibility but still get good performance, we cached vectors as bitmaps at runtime. The easiest way to do this in AS3 is to use cacheAsBitmap, but beware – when these cached display objects undergo a transformation other than x, y position, caches must be continually regenerated, hurting performance.
Fortunately Air has recently introduced an additional property ‘cacheAsBitmapMatrix’. This allows the transformations to be handled by the GPU with existing cached bitmaps. The implementation is straight forward:
displayObject.cacheAsBitMap = true;
displayObject.cacheAsBitmapMatrix = new Matrix();
Even better optimisation can be achieved by drawing graphics into bitmaps, and then removing the original elements from the display list.
For maximum performance, move away from the Flash display list completely. A custom blitting approach or Starling/Stage3D would likely yield the best results, but that didn’t seem necessary for this app, so we did not investigate such a solution.
Our general approach:
- Attach vector assets and scale based on device resolution.
- If possible draw into a bitmap and remove original asset.
- Otherwise, where appropriate use cacheAsBitmap/cacheAsBitmapMatrix
Drawing a lot of bitmaps is going to eat up memory, so releasing bitmap data as soon as it’s no longer needed is essential.
A potential downside of Air is that you cannot easily leverage the native application UI libraries such as Cocoa for iOS. There are some UI libraries for Air mobile out there, but none of them seemed right for our project. We ended up making our UI components from scratch which added dev time, although allowed for a consistent UI in both platforms.
Since text input was a key component of the app we opted to use a feature in Air called StageText. StageText allows developers to utilise native OS text entry fields (as opposed to a standard Flash input text field). StageText provides the text editing experience that users are familiar with, and supports keyboard customisation, auto capitalisation, and spell checking.
Unfortunately, presenting StageText seamlessly amongst other display list elements is tricky. Formatting capabilities for StageText instances are limited compared to AS3.
But more of an issue is handling input. A user can dismiss text entry by tapping anywhere outside of the field. On Android they can also use the device’s back button. For our project, distinguishing between these was necessary and it turns out quite difficult.
It’s a requirement for Android apps that developers handle the device’s back button throughout their apps. For the most part this is straight forward; the stage dispatches a standard keyboard event with the code Keyboard.BACK. However, if a StageText instance is active, the back button closes the soft keyboard but no keyboard event is triggered by the stage or the StageText instance.
At the core of StageText issues is the fact that there are two different systems with limited access to each one other. Hopefully Adobe will make some improvements to StageText in future releases.
It’s worth noting that some of these issues could have been avoided with an alternate UI approach. Treating StageText elements like modal windows that have to be exited before interacting with the rest of the UI or navigating elsewhere in the app, would require an extra tap, but would solve most of the problems experienced.
Adobe Native Extensions
When a native feature is not supported in Air, or a feature doesn’t work correctly, we turned to Air Native Extensions to bridge the gap. We used extensions from FreshPlanet and Distriqt. All support 64 bit which is required by Apple.
KeyboardSize (Fresh Planet) returns the size of the soft keyboard (Air’s built in methods don’t work properly on Android).
AirFlurry (Fresh Planet) to implement Flurry analytics.
Dialog (Distriqt) to present native confirmation dialog. We discovered that opening a “tel://xxxxxxx” style link on iOS Air calls the number immediately, we used the Dialog extension to pop up a confirmation dialog first.
Notifications (Distriqt) to provide local scheduled notifications.
- Vectors give flexibility for multiple resolutions but must be either cached as bitmaps or blitted to get 60fps performance.
- Seemingly simple things like text entry can cause considerably more pain in Air than in native apps or Flash for web.
- Not all projects are a good fit for Air. Apps that benefit from direct access to established UI libraries and depend on close integration with OS/hardware are probably best developed as Native Apps.
- Despite causing some hair pulling, Air’s ability to publish an app to multiple platforms makes it worthy of consideration on future projects.