Being quite new to native iOS development, and having previously tried to steer clear of anything Xcode and Objective C related, mainly because of its essay-style method calls, but also because I had to constantly try and remember how to access the hash key on my mac.
I was happy and excited when we received a brief to create an iOS mobile application using Adobe AIR.
With the release of AIR 3.2 harnessing the power of stage3D and also providing better GPU rendering, and Adobe’s recent announcement to concentrate on its mobile development platform, rather than continuing Flash browser support, AIR seemed like it was up to the challenge.
What the hell is this?
At first, it was hard not to think of a mobile device like an old desktop computer that may have been sitting in a redundant office space somewhere, processing dust, but rather as a unique device with its own set of characteristics and behavior.
In addition to the obvious performance and computational pitfalls to think about, consideration had to be given to the fact that on an iOS device, AIR applications get compiled to a native iOS executable, rather than using the JIT compiler, which is used within the Flash player. Which due to the compilation differences could potentially lead to different performance quirks.
Plan your optimisation!
There are many great optimisation articles out there, like Scots Petersen’s, or Oliver Goldman’s, which I suggest you read. Amongst all the useful optimisation tips and tricks, they specify the importance of planning your optimisations using measurements and metrics.
This is valid and invaluable information. You could find yourself spending days optimising areas of your code, such as where to place the decrement operator in a while loop, only to leave you bewildered when your application still chugs and moves your lovely ball bouncing animation across the screen one pixel at a time.
Although optimisation is key to the application’s performance, I found the most important thing to remember when developing for iOS, using AIR, is that it comes with its own set of rules and therefore it is essential to try and approach your application framework and structure differently.
Chunk, Chunk, Chunk away…
From my experience, “chunking”, which is often overlooked is a pivotal factor to consider when structuring your application. If executed correctly it can help break up computations into short-running segments, increasing overall performance, freeing up the CPU.
It can be that tiny difference between having a dream-like experience, or that nightmare where you are falling only to be woken with a jolt not knowing where you are or what happened. So break up your excessive computations where possible and try to reduce your initial asset load.
Speaking of Loading, please wait
Using AIR with iOS does not currently provide support for loading in external SWF files. Consequently, I found there are two main options at your disposal when you are planning for your application to have assets at startup, rather than purely relying on an internet connection. If it does, I suggest you perhaps rethink your approach before your application shows up blank due to a misplaced Internet connection.
One option would be to embed all the assets paths in a static class such as an AssetLibrary.as, then reference all your assets from there, packaging all your external files with your AIR deployment and loading them in from disk at runtime. The other option, which I found reduced the loading time by at least half, is to create a SWC with all the assets compiled into it. This method also proved faster than loading in the assets directly from disk and reduced the IPA size dramatically.
Bitmap that Data
If you are making anything visually pleasing, you really should be running your application in GPU mode. Try using bitmaps and BitmapData where possible. Convert any display objects you can into bitmaps, which will significantly improve performance and will reduce the number of data references needed to be stored in memory.
I find the best approach is to create interactive hotspots that listen for touch events and determine the appropriate action to take based on the state of the application and the location of the touch. This reduces the number of event listeners to a single object, further increasing performance.
If you find yourself in a situation where you must use interactive objects or vectors, there are two methods available to which will help you improve your rendering performance: these are CatchAsBitmap and CacheAsBitmapMatrix. Ross Przybyisk has a good article explaining these, so make sure you have a clear understanding of when and where to use them, as you could end up adding further fat to your GPU sandwich.
Stop having children
Adding and removing children to the display list significantly effects performance and increases memory usage, similar to creating and removing objects. Try to use object pooling where possible, reuse objects, avoid removing them from the display list; simply toggle their visibility when needed.
The Adobe optimisation guide has a good explanation of this and many other interesting optimisation tips.
Has the garbage man been?
As fun as it may be to play around with your newly found garbage collection power, try not to overdo it with your excitement. Being able to force garbage collection in AIR with System.GC() gives you great power, so use it responsively as it will nibble at the CPU each time it’s used. This may not seem like a significant amount, but if you have a thousand other computations queued up it will noticeably effect performance.
AIR actually handles its own garbage collection pretty well therefore use sparingly and only force garbage collection when a large removal of objects is necessary or to free up memory, while also remembering your chunking.
Are you listening?
Gesture and touch events may seem like overkill if you are making a small application, especially as mouse events can be used to the same effect. They do however provide extra functionality geared towards mobile development and are optimised to reduce memory consumption.
I however strongly recommend using Robert Penner’s Signals, because they are faster and more lightweight than events. Where you can, try to reduce your use of events or alternatively append events to objects, then simply dispatch signals from their handlers remembering each time to use stopImmediatePropagation() to prevent bubbling.
ByteArray and back again
Rather than just optimising your code, you also have the ability to optimise your code’s genetic makeup. Converting your classes and or assets into ByteArray and then back again at runtime could save you those extra little bits of memory, and computational calories trying to slim down your already overweight CPU.
Ralph Hauwert has written a nice class that decodes images optimised using WEB.P which could come in useful if you are building an image heavy application.
Test your application on device. Emulators are great, but nothing beats the real thing. You will often experience performance and memory differences between emulator and device so make sure you spot these before you are in too deep.
If you can, try to use the Xcode profiler, I found it to be much more useful than FDT when testing and debugging my app.
Last minute thoughts
Harnessing the power of AIR for mobile is an art in itself and cannot be underestimated. Your approach must be thorough and well thought out, with memory consumption at the forefront of your mind.
Soon with AIR and its ever-growing advancements, access to native extensions and use of frameworks like Starling there will be nothing you won’t be able to do. Although, as with all applications, the technology must fit the purpose and sometimes native iOS development could be better suited to your project.
Looking to the future I think it will be important to build a stronger bond with native iOS development and frameworks like Sparrow. Having good knowledge of both platforms will improve your abilities to use them in unison and better harness AIR’s Native extension capability.