Yesterday Adobe announced AIR 2.6, which allows you to compile to iOS and is really fast.
Before you start, I recommend you to read (or at least bookmark) these articles:
- Developing for iOS with AIR 2.6
- Adobe AIR 2.6 Developer Release Notes
- Writing multiscreen AIR apps
- Authoring mobile Flash content for multiple screen sizes
- Developing for both retina and non-retina iOS screens using AIR 2.6
John Lidquist wrote a brilliant batch script generator which helped me to start: AIR4AIR. Thanks John!
You definitely should to read John’s article Top 10 AIR 2.6 for iOS Questions.
It is somewhat painful to solve initial compilation steps, so I hope my Ant script will help you get through. Basically it allows you to compile both desktop AIR and an iPhone4 (retina) IPA file. All you need is an iOS developer certificate and provisioning profile. The script then creates the rest (folder structure, desktop AIR certificate). But please take a look at the source and edit it as needed – e.g. there’s only iphone-retina descriptor and settings, but it should be pretty straight-forward to add what you need.
Ant script
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2011 Vaclav Vancura (http://vancura.org)
-->
<project default="1. Create directories" name="Test AIR 2.6 Ant script">
<!-- Edit me: Ant script name -->
<tstamp>
<format property="timestamp" pattern="yyyy-MM-dd_HH-mm-ss" />
</tstamp>
<!-- FLEX SDK configuration -->
<property name="flex_sdk_dir" value="/Developer/SDKs/FlexSDK41" />
<!-- Edit me: path to your Flex SDK with AIR 2.6 overlaid -->
<property name="mxmlc_path" value="${flex_sdk_dir}/lib/mxmlc.jar" />
<property name="adt_path" value="${flex_sdk_dir}/lib/adt.jar" />
<!-- Application configuration -->
<property name="app_name" value="Test Application" />
<!-- Edit me: Name of the app -->
<property name="main_source" value="src/Main.as" />
<!-- Edit me: Main bootstrap class -->
<property name="debug_dir" value="bin" />
<property name="assets_dir" value="assets" />
<property name="certificates_dir" value="certificates" />
<property name="deploy_dir" value="deploy" />
<property name="libs_dir" value="libs" />
<property name="swf_filename" value="${debug_dir}/test.swf" />
<!-- Edit me: compiled SWF filename -->
<!-- Desktop certificate configuration -->
<property name="desktop_certificate_org_name" value="Your Name" />
<!-- Edit me: your name, probably not Your Name -->
<property name="desktop_certicitace_org_unit" value="" />
<!-- Edit me: organization unit -->
<property name="desktop_certificate_name" value="your-name" />
<!-- Edit me: your name (this should be a filename safe name) -->
<property name="desktop_certificate_password" value="password" />
<!-- Edit me: your certificate password -->
<property name="desktop_certificate_country_code" value="CZ" />
<!-- Edit me: country code -->
<property name="desktop_certificate_key_type" value="2048-RSA" />
<!-- Desktop configuration -->
<property name="desktop_certificate_filename" value="${certificates_dir}/${desktop_certificate_name}.pfx" />
<property name="desktop_air_filename" value="${deploy_dir}/${app_name}_${timestamp}.air" />
<property name="desktop_descriptor" value="${assets_dir}/desktop-app.xml" />
<!-- iOS configuration -->
<property name="ios_target" value="ipa-test" />
<!--
Edit me: choose one from:
* ipa-test - Quick publishing for device testing
* ipa-debug - Quick publishing for device debugging
* ipa-ad-hoc - Deployment - Ad hoc
* ipa-app-store - Deployment - Apple App Store
-->
<property name="ios_provisioning_profile_path" value="/Users/Test/Library/MobileDevice/Provisioning Profiles/your-provisioning-profile.mobileprovision" />
<!-- Edit me: your provisioning profile path -->
<property name="ios_certificate_filename" value="/Users/Test/Documents/iphone-developer.p12" />
<!-- Edit me: your iphone developer certificate, see http://j.mp/convert-profiles -->
<property name="ios_certificate_password" value="password" /> <!-- Edit me: your certificate password -->
<property name="ios_ipa_filename" value="${deploy_dir}/${app_name}_${timestamp}.ipa" />
<property name="ios_iphone_retina_descriptor" value="${assets_dir}/iphone-retina-app.xml" />
<!-- Add more descriptor files here -->
<!-- Create required directories -->
<target name="1. Create directories">
<mkdir dir="${assets_dir}" />
<mkdir dir="${deploy_dir}" />
<mkdir dir="${debug_dir}" />
<mkdir dir="${libs_dir}" />
<mkdir dir="${certificates_dir}" />
</target>
<!-- Creating a digital ID certificate -->
<target name="2. Create temporary desktop certificate">
<java jar="${adt_path}" fork="true">
<arg line="-certificate" />
<arg line="-cn ${desktop_certificate_name}" />
<arg line="-ou '${desktop_certicitace_org_unit}'" />
<arg line="-o '${desktop_certificate_org_name}'" />
<arg line="-c '${desktop_certificate_country_code}' ${desktop_certificate_key_type} ${certificates_dir}/${desktop_certificate_name}.pfx ${desktop_certificate_password}" />
</java>
</target>
<!-- Compile SWF to build directory for desktop packaging -->
<target name="3. Compile" depends="1. Create directories">
<java jar="${mxmlc_path}" fork="true" failonerror="true">
<arg value="-debug=false" />
<arg value="-optimize=true" />
<arg value="+flexlib=${flex_sdk_dir}/frameworks" />
<arg value="+configname=air" />
<arg value="-file-specs=${main_source}" />
<arg value="-output=${swf_filename}" />
<arg value="-l+=${libs_dir}" />
</java>
</target>
<!-- Packaging the application to an air-file in the publish directory -->
<target name="Package for desktop" depends="3. Compile">
<java jar="${adt_path}" fork="true" failonerror="true">
<arg line="-package" />
<arg line="-storetype pkcs12" />
<arg line="-keystore '${desktop_certificate_filename}'" />
<arg line="-storepass '${desktop_certificate_password}'" />
<arg line="'${desktop_air_filename}'" />
<arg line="'${desktop_descriptor}'" />
<!-- Add folders to be bundled in the AIR file here -->
<arg line="'${swf_filename}'" />
<arg line="'${assets_dir}/icons/icon16.png'" />
<arg line="'${assets_dir}/icons/icon32.png'" />
<arg line="'${assets_dir}/icons/icon48.png'" />
<arg line="'${assets_dir}/icons/icon128.png'" />
</java>
</target>
<!-- Compile SWF to build directory for iOS packaging -->
<target name="Package for iOS (iPhone retina)" depends="3. Compile">
<java jar="${adt_path}" fork="true" failonerror="true">
<arg line="-package" />
<arg line="-target ${ios_target}" />
<arg line="-storetype pkcs12" />
<arg line="-keystore '${ios_certificate_filename}'" />
<arg line="-storepass '${ios_certificate_password}'" />
<arg line="-provisioning-profile '${ios_provisioning_profile_path}'" />
<arg line="'${ios_ipa_filename}'" />
<arg line="'${ios_iphone_retina_descriptor}'" />
<!-- Add folders to be bundled in the AIR file here -->
<arg line="'${swf_filename}'" />
<arg line="'${assets_dir}/icons/icon29.png'" />
<arg line="'${assets_dir}/icons/icon48.png'" />
<arg line="'${assets_dir}/icons/icon57.png'" />
<arg line="'${assets_dir}/icons/icon72.png'" />
<arg line="'${assets_dir}/icons/icon512.png'" />
</java>
</target>
</project>
Gisted here: https://gist.github.com/882808
Download
You may need to download all files including example descriptors, if you don’t have it yet. Here we go.






















Mobile AIR optimizations.
If you’re interested in optimization of Adobe Flash Platform, mainly AIR for mobile devices, you may be interested in the outline I compiled from several sources yesterday:
Device features
General
Don’t write code to constructors, the JIT (Just In Time compiler) won’t optimize it. Code inside the constructor is not optimized by the Just-in-time compiler (JIT). To use JIT optimized code there is the possibility to call a function out of the constructor. The code inside that function is then optimized again. The reason why there are no test results here is that I could not be a real rule when it makes sense to do this or not. Usually you expect a faster code execution but if there is no difference because the JIT is not used at all which could happen you have an even slower code execution because of one extra function call which is your initializing function. (Joa Ebert: ActionScript 3 optimization techniques)
If you need to use an Array of Numbers, use ByteArray. It’s much faster.
Reuse everything, mainly DisplayObjects and URLLoader objects (Reusing Objects)
Use ScrollRect instead of masks.
Create DisplayObjects ahead of time to increase perceived speed of transitions.
Typing prevents runtime evaluation of method signatures which can slow down the VM
This applies to web service JSON data too:
[ { "_type_" : "RecordClass", "name" : "Joe" } ]Use Object pooling
Free memory when not needed
Use single BitmapData reference
Learn about MipMapping
Learn about 3D effects
GPU vs. CPU
Events
Mouse
Don’t use MouseEvent.MOUSE_MOVE. It is simply not there.
Check for mouse position in an interval. But no setInterval or Timer, see below.
One other thing you can do is prevent mouse events (that are set to bubble by default) from effecting children you weren’t intending. Try setting mouseChildren and mouseEnabled to false.
Filters & Blending Modes
Visibility
visible = false is not enough. Use removeChild().
But first stop the MovieClip.
Timers
Don’t use Timers: To improve performance avoid high rate timers, this will reduce the amount of events being fired and the quantity of code being executed. The second performance increase is to have one timer for the entire application. Think of it as a heartbeat for your application keeping the internal timing for all your functions. This will reduce the amount of timers you have and the amount of time calculations taken from the Flash Player environment.
Event.ENTER_FRAME is better than Timer.
Set frame rate to 4 when not animating, increase when needed. Monitoring your frame rate is very important as many flash designers want to ramp up the frame rate to 60 frames per second because it will make the animations “pretty”.
Use deactivation when the app is in background:
Grant Skinner wrote a brilliant class on FPS management.
Vectors
Draw vector content to Bitmap before it gets rendered to Stage.
For simple shapes that are not interactive, use Shape objects. For interactive objects that don’t need a timeline, use Sprite objects. For animation that uses a timeline, use MovieClip objects. Always choose the most efficient type of object for your application.
Use the getSize() method to benchmark code and determine the most efficient object for the task.
Avoid using the ActionScript drawing API (the Graphics class) to create graphics. When possible, create those objects statically at authoring time instead.
Pre-cache Vector animations as a Sprite Sheet for optimal load times. Instead of caching your vectors as bitmaps at runtime, you should convert your vectors into bitmaps before compiling your game. First, store your bitmaps in a tile-based sprite sheet. Then, export this image for action script as bitmap data. Finally, cache positions (parts of the bitmap data) you want to manipulate as rectangles and use the copyPixel method to pull the actual bitmap data off the sprite sheet. 8BitRocket’s website covers how to achieve all of these steps except for converting your MovieClip based animations into a single sprite sheet image. It so happens that I have developed a component that will automatically export a series of MovieClip animations into a PNG with transparency capability; I am willing to share this if I can get some support from other developers on this forum.
Bitmaps
Scale bitmap assets to the final size before importing them.
One other little used cache method is cacheAsBitmapMatrix. Similar to the cacheAsBitmap this will also cache the x, y, rotation, scale, skew properties. Keeping the Flash Player from having to redraw the object. Again, if you make any changes to alpha/color or the children sprites matrix the object will be redrawn and you will be spending more time and memory to cache an object that is constantly redrawn.
Don’t forget that images split by the power of 2 are going to be more efficient on memory than odd sized images. This can’t always be avoided by it’s good to remember.
Make bitmaps in sizes that are close to, but less than, 2n by 2m bits. The dimensions do not have to be power of 2, but they should be close to a power of 2, without being larger. For example, a 31-by-15–pixel image renders faster than a 33-by-17–pixel image. (31 and 15 are just less than powers of 2: 32 and 16.)
If possible, set the repeat parameter to false when calling the Graphic.beginBitmapFill() method.
Runtime bitmap caching is impractical for mobile. Ideally, I’d like to pre-cache all of my vector based animations as bitmaps such that they are sized to the native resolution of my device at runtime (ex, iPhone has very small resolution, iPad large resolution, and Android, right in the middle). 8BitRocket’s tutorials explain how to achieve this effect by running a timer to loop through a time-line based vector animation to draw each frame as a bitmap and store it within an array as bitmap data for later use. However, I have discovered that attempting to cache a large number of vector frames as bitmaps within an array at runtime on any mobile device is utterly impractical. A single unit model with 300 unique animation frames at 100×100 pixels might take 15-20 seconds for a mobile device to cache. A game with twenty plus models would take 5+ minutes to load, and not even the most diehard players will be willing to wait that long.
Sprites
Write some method that splits your bitmap sheet image to single frames with copyPixels and store them to Vector. (and dispose the sheet).
Next start extending Bitmap class where you pass your vector and create MovieClip style methods like gotoAndPlay() and gotoAndStop in which you then assign correct BitmapData to your extended Bitmap depending from the function.
Also notice that events are extremely slow so you might want to push all your currently used display objects to some array and move/animate them on single Event.ENTER_FRAME outside your extended Bitmap.
Since Bitmap can’t use mouse events you also need to add that functionality to your extended bitmap class. Simply use hitTestPoint to determine if mouse was over the bitmap when clicked or dragged.
And you may also want to use object pooling (if you’re doing more fast paced games) since creating and removing instances on the fly is also sadly slow.
Text
Flash Player 10 and AIR 1.5 introduced a powerful new text engine, the Adobe Flash Text Engine (FTE), that conserves system memory. However, the FTE is a low-level API that requires an additional ActionScript 3.0 layer on top of it, provided in the flash.text.engine package.
For read-only text, it’s best to use the Flash Text Engine, which offers low memory usage and better rendering. For input text, TextField objects are a better choice, because less ActionScript code is required to create typical behaviors, such as input handling and word-wrap.
In GPU rendering mode, text fields are not always moved to a visible location when the virtual keyboard opens. To ensure that your text field is visible while the user enters text, do one of the following. Place the text field in the top half of the screen or move it to the top half of the screen when it receives focus.
Video
When encoding your video thing of the final endpoint. Most devices have a native codec (usually H.264) that will help improve decompression and playback on the hardware level rather than the software level. Make sure to target these native codecs and see the performance and battery life improvement.