
Contact me if you’re there: @derRaab
Adobe recently released AIR 2.6 with improved iOS support, so I finally had to get into using ADT with ANT. My editor of choice is FDT so I wanted to do as less extra work as possible. Mainly because I’m no terminal-guy. I need a clean GUI holding my hand while setting up workspaces, linking libraries and stuff like that. In other words, command line and compiler arguments are freaking me out.
I read a lot of blogposts and articles (see link list on the bottom of this post) but most of them compile SWFs using ANT, which means setting source path and stuff like that as command line arguments. But hey, FDT does this already during my daily workflow, so to me it seems natural reusing this within the iOS packaging process.
So I won’t comment a lot what I came up with because all of this can be read on one of the sites below, but show you simply a screenshot of my “IOSAIRTest” workspace structure and of course the ANT files. Notice that I’m not into having different directories for debug, publish, testing and so. I like to have all source files clean and separated by file type (would have an mxml folder too):
You will find the most interesting files in src/ant. Let’s start with local.properties which just defines the SDK path:
FLEX_HOME=/Users/{USERNAME}/Path/To/FlexSDKs/4.5.0.17689_AIR_2.6
MXMLC=${FLEX_HOME}/bin/mxmlc
ADT=${FLEX_HOME}/bin/adtWithin build.properties you setup all params regarding your project:
app.rootdir=./../..
app.descriptor=${app.rootdir}/bin/IOSAIRTest-app.xml
app.rootfile=IOSAIRTest.swf
app.sourcedir=${app.rootdir}/src/as
app.bindir=${app.rootdir}/bin
app.source=${app.sourcedir}/de/superclass/IOSAIRTest.as
app.includes=assets icons Default.png Default-Portrait.png
build.storetype=pkcs12
build.keystore=${app.rootdir}/resources/ios/iPhoneDevCert.p12
build.storepass={PASSWORD;)}
build.mobileprofile=${app.rootdir}/resources/ios/AIR_TEST.mobileprovision
build.name=IOSAIRTest.ipa
fdt.projectname=AIRTest
fdt.mainclass=${app.source}
fdt.target=${app.bindir}/${app.rootfile}And build.xml contains four ways to create the IPA package and the according FDT tasks:
<?xml version="1.0" encoding="UTF-8"?> <project name="AIR export" basedir="."> <property file="local.properties" /> <property file="build.properties" /> <!-- FDT tasks see http://fdt.powerflasher.com/docs/FDT_Ant_Tasks#fdt.launch.application - for documentation see http://fdt.powerflasher.com/docs/FDT_and_Ant_Tutorial#Your_First_Task:_Compiling_.26_JRE_Error - if you run into errors! --> <target name="FDT create SWF"> <fdt.launch.resetFlexCompiler/> <fdt.launch.application projectname="${fdt.projectname}" mainclass="${fdt.mainclass}" profile="false" debug="false" target="${fdt.target}" startswf="false"/> </target> <target name="FDT create SWF debug"> <fdt.launch.resetFlexCompiler/> <fdt.launch.application projectname="${fdt.projectname}" mainclass="${fdt.mainclass}" profile="false" debug="true" target="${fdt.target}" startswf="false"/> </target> <!-- ADT tasks --> <target name="iOS create IPA debug" depends="FDT create SWF debug"> <exec executable="${ADT}"> <arg line="-package -target ipa-debug -storetype ${build.storetype} -keystore ${build.keystore} -storepass ${build.storepass} -provisioning-profile ${build.mobileprofile} ${app.bindir}/${build.name} ${app.descriptor} -C ${app.bindir} ${app.rootfile} ${app.includes} "/> </exec> </target> <target name="iOS create IPA test" depends="FDT create SWF"> <exec executable="${ADT}"> <arg line="-package -target ipa-test -storetype ${build.storetype} -keystore ${build.keystore} -storepass ${build.storepass} -provisioning-profile ${build.mobileprofile} ${app.bindir}/${build.name} ${app.descriptor} -C ${app.bindir} ${app.rootfile} ${app.includes} "/> </exec> </target> <target name="iOS create IPA ad-hoc" depends="FDT create SWF"> <exec executable="${ADT}"> <arg line="-package -target ipa-ad-hoc -storetype ${build.storetype} -keystore ${build.keystore} -storepass ${build.storepass} -provisioning-profile ${build.mobileprofile} ${app.bindir}/${build.name} ${app.descriptor} -C ${app.bindir} ${app.rootfile} ${app.includes} "/> </exec> </target> <target name="iOS create IPA app-store" depends="FDT create SWF"> <exec executable="${ADT}"> <arg line="-package -target ipa-app-store -storetype ${build.storetype} -keystore ${build.keystore} -storepass ${build.storepass} -provisioning-profile ${build.mobileprofile} ${app.bindir}/${build.name} ${app.descriptor} -C ${app.bindir} ${app.rootfile} ${app.includes} "/> </exec> </target> </project>
If you’re not sure how to get started with all this AIR 2.6 stuff because it’s currently not integrated in the Flex SDKs – follow this steps:
-target-player=11 to access the new APIs (more)Loads of linked informations:
http://blogs.adobe.com/cantrell/archives/2011/03/how-to-use-air-2-6-with-flash-builder-4.html
http://www.mobilerevamp.org/2010/07/30/how-to-build-your-first-air4android-application-using-fdt-and-eclipse/
https://code.google.com/p/air-on-android-with-fdt/
http://www.beautifycode.com/flex-hero-mobile-project-template-for-fdt-4-2
http://www.beautifycode.com/publish-package-an-air-file-with-fdt4
http://labs.almerblank.com/2011/03/using-ant-to-compile-a-flex-mobile-project-for-ios/
http://va.lent.in/blog/2011/03/25/air2-6-app-for-ios/ (Thanks for ANT files!)
http://developerartofwar.com/2011/03/24/air-2-6-on-ipad-2-in-15-mins/
http://karoshiethos.com/2010/04/06/use-fdt-folder-path-variables-in-ant/
http://fdt.powerflasher.com/docs/FDT_Ant_Tasks#fdt.launch.application
http://labs.almerblank.com/2011/03/using-ant-to-compile-a-flex-mobile-project-for-ios/
Update:
http://www.blackcj.com/blog/2011/04/04/ios-android-and-blackberry-in-a-single-click-with-ant/
I’m always searching for it so here is a useful blogpost.
As the latest FDT version ( Beta 4 ) does not set compiler arguments concerning SWF-size, framerate etc. by default I started using SWF-Metatags to specify these settings.
I’m talking of these things:
[SWF(width="910", height="610", backgroundColor="#FFFFFF", frameRate="40")]Just set this tag above your class opening in the one you’re going to compile. Here is a complete list of (useful) arguments you can pass in:
width
widthPercent
height
heightPercent
scriptTimeLimit
frameRate
backgroundColorSo basically this is the fastest way to set up common compile-arguments to your project. By the way there is no Flex-Framework required – these tags also work in pure AS3 projects.
Using Date.setTime() and Date.getTime() is faster than Date.time.
My quick speed test to determine the fastest way to get and set time values on Date objects:
var date : Date = new Date(); var max : Number = date.time; var start : int; var end : int; var time : Number; var times : Vector.<Number> = new Vector.<Number>(); var c : int = 1000000; for ( var i : int = 0; i < c; i++ ) { times.push( Math.round( max * Math.random() ) ); } start = getTimer(); for ( i = 0; i < c; i++ ) { date.time = times[ i ]; }; end = getTimer(); trace( "date.time = times[ i ];", end - start ); start = getTimer(); for ( i = 0; i < c; i++ ) { date.setTime( times[ i ] ); }; end = getTimer(); trace( "date.setTime( times[ i ] );", end - start ); start = getTimer(); for ( i = 0; i < c; i++ ) { time = date.time; }; end = getTimer(); trace( "time = date.time;", end - start ); start = getTimer(); for ( i = 0; i < c; i++ ) { time = date.getTime(); }; end = getTimer(); trace( "time = date.getTime();", end - start ); // Result: // // date.time = times[ i ]; 381 // date.setTime( times[ i ] ); 270 // time = date.time; 184 // time = date.getTime(); 65
Everything New in Adobe AIR 2.6 (read all on Christian Cantrells blog):
- Asynchronous Bitmap Decoding
- Owned Windows
- Bitmap Capture in StageWebView
- Microphone support on iOS
- StageWebView on iOS
- Multitasking on iOS
- Retina Support on iOS
- iOS Camera, CameraUI, and CameraRoll Support
- Improved hardware acceleration on iOS
- PFI is now ADT
- Configurable panning with soft keyboard activation
- Programmatic control of the display of the on-screen keyboard
- Support for the Amazon Android Market (more info)
- Vector printing on Linux
- Native cursor support
- On-device debugging over USB (Android only)
- Native Menu event refinement
- Enhanced text support on Android
- NetConnection.httpIdleTimeout
- Bundled Android USB drivers on Windows
- Support for the vipaccess:// URI
- -version flag for ADT
Get runtime and get SDK.
Video – developing for iOS with AIR for mobile 2.6/
I really hate Flash Player updates just because it always takes me some time to figure out again how to update all of them on my Mac. Why isn’t there some kind of centralized Flash Player Manager?
Well, as normal user it’s really easy. Just visit Adobe and you’re almost done. But for me as developer I need to update all players used by Flash CS 5, Flash Builder 4 and the Flex SDKs as well. Note to myself: Just remember these steps (in this case for 10.2):
Visit the Flash Player Download Heaven.
Update Browser Debug Player:
Update Adobe Flash CS 5 Release Projector for Mac OS X:
Update Adobe Flash CS 5 Debug Projector for Mac OS X:
Update Adobe Flash CS 5 Release Projector for Windows:
Update Adobe Flash CS 5 Debug Projector for Windows:
Well, there is one issue I don’t understand, but I think everything is working properly. I created a little file running this script:
import flash.system.Capabilities; import flash.text.TextField; import flash.text.TextFieldAutoSize; var textField : TextField = new TextField(); textField.autoSize = TextFieldAutoSize.LEFT; textField.text = Capabilities.version; addChild( textField );
But when I’m just “testing” in Flash CS 5 it still says “MAC 10,1,52,14″. When I’m “debugging” it says “MAC 10,2,152,26″. I removed all Flash Player.app and Flash Player Debugger.app except the latest. If you have a clue – let me know.
Just for me – Flash Player Updates I manually installed lately:
Since I’m using the WP-Syntax plug-in on my blog I tried to optimize font size and the tab width. Like a lot of developers I heavily use tabs in my source code. These tabs are really wide within the browser so I was looking for a way to minimize the used space.
I’m not a CSS guy but it seems like there is no way to set a specific tab width. So why not replace the tab with white space?
My change to wp-syntax/wp-syntax.php:
function wp_syntax_highlight($match) { global $wp_syntax_matches; $i = intval($match[1]); $match = $wp_syntax_matches[$i]; $language = strtolower(trim($match[1])); $line = trim($match[2]); $escaped = trim($match[3]); $code = wp_syntax_code_trim($match[4]); // INSERT JUST THIS LINE OF CODE $code = str_replace ( ' ' , ' ' , $code ); . . .
While diving into the Flex 4 spark framework I ran into a problem using
mx.managers.DragManager.doDrag() with
mx.managers.DragManager.acceptDragDrop():
If you just drag, accept on dragDrop and then drop without moving the mouse you’ll get this error:
TypeError: Error #1009:
at mx.managers.dragClasses::DragProxy/mouseUpHandler()
[E:\dev\4...\mx\managers\dragClasses\DragProxy.as:591]
This is, because DragProxy didn’t receive a MouseEvent yet. So what you can do is manually send one. See this little use case and test for yourself:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" applicationComplete="construct()" height="100%" width="100%"> <fx:Script> <![CDATA[ import mx.controls.Alert; import mx.core.DragSource; import mx.core.UIComponent; import mx.managers.DragManager; private function construct() : void { // Get notified whan a global error occurs loaderInfo.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, errorHandler ); } private function onButtonMouseDown( event : MouseEvent ) : void { var button : UIComponent = UIComponent( event.target ); var dragSource:DragSource = new DragSource(); DragManager.doDrag( button, dragSource, event ); // Accept dragDrop if ( acceptDragDropCheckBox.selected ) { DragManager.acceptDragDrop( this ); } // Avoid error if ( avoidErrorCheckBox.selected ) { button.dispatchEvent( new MouseEvent( MouseEvent.MOUSE_MOVE, true,false, button.mouseX, button.mouseY ) ); } } protected function errorHandler( e : UncaughtErrorEvent ) : void { Alert.show("An error has occurred and been caught by the global error handler: " + String( e.error ), "My Global Error Handler"); e.preventDefault(); } ]]> </fx:Script> <s:Label x="20" y="10" text="If you accept dragDrop you need to avoid the error!"/> <s:CheckBox id="acceptDragDropCheckBox" x="20" y="30" label="Accept DragDrop"/> <s:CheckBox id="avoidErrorCheckBox" x="20" y="50" label="Avoid error"/> <mx:Button id="dragButton" x="20" y="110" mouseDown="onButtonMouseDown(event)" label="Drag me. Or click me for error!"/> </s:Application>
UPDATE:
A much better way to avoid this error is listening to a DragEvent.DRAG_ENTER before calling DragManager.acceptDragDrop():
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" dragEnter="dragEnterDataRenderer(event)" height="100%" width="100%"> <fx:Script> <![CDATA[ import mx.core.DragSource; import mx.core.UIComponent; import mx.events.DragEvent; import mx.managers.DragManager; private function onButtonMouseDown( event : MouseEvent ) : void { var button : UIComponent = UIComponent( event.target ); var dragSource:DragSource = new DragSource(); DragManager.doDrag( button, dragSource, event ); } private function dragEnterDataRenderer( event : DragEvent ) : void { DragManager.acceptDragDrop( this ); } ]]> </fx:Script> <mx:Button id="dragButton" x="20" y="20" mouseDown="onButtonMouseDown(event)" label="Click me. Drag me. No Error."/> </s:Application>
I really need to quote this bit-101 blog post.
It’s the first time I heard about that great website:
I don’t have much more to say about this. Do you need to learn or brush up on any subject related to math? You will find a wealth of data here in a massive number of videos, each 10 minutes or under. Seriously, anything you want to learn is here, from the basics to advanced calculus. Where does this guy find the time?
I got the link from Brook Andrus’ site, where she gives a bit more info on the author. Pretty amazing guy.
http://www.brooksandrus.com/blog/2010/10/01/the-wrath-of-khan-the-genesis-of-21st-century-education
UPDATE: There’s another one: http://patrickjmt.com/
I was thinking about not creating linked lists for every data type I use but using one abstract data structure containing my specific data types.
So I wrote this short performance check:
package de.superclass { import flash.display.Sprite; import flash.events.Event; import flash.events.TimerEvent; import flash.utils.Timer; import flash.utils.getTimer; public final class LinkedListTest extends Sprite { private var _abstractItemLinkedList : AbstractItem; private var _dataItemLinkedList : DataItem; private var _vector : Vector.<DataItem>; public function LinkedListTest() { loaderInfo.addEventListener( Event.COMPLETE, onLoaderInfoComplete ); } private function onLoaderInfoComplete( event : Event ) : void { createData( ); var timer : Timer = new Timer( 3000, 1 ); timer.addEventListener( TimerEvent.TIMER_COMPLETE, onTimerComplete ); timer.start(); } private function onTimerComplete(event : TimerEvent) : void { runTest( ); } private function createData() : void { var vector : Vector.<DataItem> = _vector = new Vector.<DataItem>(); for ( var i : int = 0; i < 5000000; i++ ) { var dataItem : DataItem = new DataItem(); dataItem.index = i; vector.push( dataItem ); var abstractItem : AbstractItem = new AbstractItem(); abstractItem.data = dataItem; if ( i == 0 ) { _dataItemLinkedList = dataItem; _abstractItemLinkedList = abstractItem; } else { previousDataItem.next = dataItem; previousAbstractItem.next = abstractItem; } var previousDataItem : DataItem = dataItem; var previousAbstractItem : AbstractItem = abstractItem; } } private function runTest() : void { var dataItem : DataItem; var startTime : int; var vector : Vector.<DataItem> = _vector; var c : int = vector.length; startTime = getTimer(); for ( var i : int = 0; i < c; i++ ) { dataItem = vector[ i ]; dataItem.index = dataItem.index; } var durationVector : int = getTimer() - startTime; startTime = getTimer(); dataItem = _dataItemLinkedList; while ( dataItem ) { dataItem.index = dataItem.index; dataItem = dataItem.next; } var durationDataItemLinkedList : int = getTimer() - startTime; startTime = getTimer( ); var abstractItem : AbstractItem = _abstractItemLinkedList; while ( abstractItem ) { dataItem = abstractItem.data; dataItem.index = dataItem.index; abstractItem = abstractItem.next; } var durationAbstractItemLinkedList : int = getTimer() - startTime; startTime = getTimer( ); abstractItem = _abstractItemLinkedList; while ( abstractItem ) { dataItem = DataItem( abstractItem.data ); dataItem.index = dataItem.index; abstractItem = abstractItem.next; } var durationAbstractItemLinkedListWithCast : int = getTimer() - startTime; startTime = getTimer( ); abstractItem = _abstractItemLinkedList; while ( abstractItem ) { dataItem = abstractItem.data as DataItem; dataItem.index = dataItem.index; abstractItem = abstractItem.next; } var durationAbstractItemLinkedListWithAs : int = getTimer() - startTime; trace( "Durations:" ); trace( "Vector", durationVector ); trace( "DataItem LinkedList", durationDataItemLinkedList ); trace( "AbstractItem LinkedList", durationAbstractItemLinkedList ); trace( "AbstractItemLinkedList Using Cast", durationAbstractItemLinkedListWithCast ); trace( "AbstractItemLinkedList Using As", durationAbstractItemLinkedListWithAs ); } } } class AbstractItem { public var data : *; public var next : AbstractItem; } class DataItem { public var index : int; public var next : DataItem; }
Results on my MacBook Pro i7:
Durations: Vector 91 DataItem LinkedList 53 AbstractItem LinkedList 76 AbstractItemLinkedList Using Cast 108 AbstractItemLinkedList Using As 121
Alsways use explicit implemented linked lists!