Creating a AIR Web Service Package with Flash and Actionscript 3.0 – Base Service Class And Simple Twitter API

by Reynaldo Columna 12 views7

base-service-class-and-simple-twitter-api

In this series of tutorials, you will learn the basics of architecting a simple but powerful Web Services Package. We’ll start off by creating a class which will be the base of all our services and then continue to create a very simple Twitter API.

Please note that due to changes in the Twitter cross domain policy, the API we will create in this tutorial will not work online, but works perfectly in an AIR application.

Requirements

Adobe Flash CS3

Try / Buy

Adobe Intergrated Runtime (AIR) for Flash CS3

Download

Source Files

Download

Pre-Requesites

Some experience in creating classes in AS3, if you dont know how to, don’t worry, you’ll learn here 😉

Create the package

The first thing we need to do before actually coding anything is to create our class package. Creating a class package is very simple, we’ll create it right on our desktop. All you have to do is create a folder and name it “com”, that will be the main package. inthe “com” folder, create another folder identifying the name of your webservices collection, I’ll call mine “reyco1”. now in the “reyco1” folder create another folder called “webservices”. That’s it! package creation complete. Now how easy was that? Very!

Now create create an Actionscript file, name it “WebService.as” and save it in the “webservices” folder. This is the class that all our apis, such as Twitter API will use to connect to their respective web services. Crack that puppy open and lets get to the coding goodness!

Coding the main WebService class

All AS3 classes are contained within a package. We already created our package in the first step, basically a few nested folders. So we’ll start our class as so:

package com.reyco1.webservice
{

}

Now we need to import the classes that we need from flash in order to make our class work:

package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;
}

Our class will extend the “EventDispatcher” class which means that we will be able to dispatch much needed events. It will have additional properties and methods which we will include in later steps. In the meantime your class should look like this:

package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

    }

}

Now we create the variables that we will need in order to connect to the online web services. We’ll need a URLRequest, a URLLoader and a URLVariables along with a generic data variable that will hold information returned from the web service. We want to make this data variable public so that it can be accessed from outside the class:

package com.helix.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

    }

}

Now that we have all the variables we need, we can go ahead and create our constructor. The class constructor is a special method always named the same name as the class. Constructors can accept arguments or not. Our’s will accept one, an HTTP Request (I’ll explain in a bit). We’ll call the EventDispatcher constructor by calling the “super” method and then set our request variable to accept the HTTP Request:

package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

		public function WebService($servicePath:String)
		{
        	super();
			request = new URLRequest($servicePath);
		}
    }
}

Now we’ll create our one and only method. This is the method that will make the call to the webservice. Our method will accept 2 arguments: the first is a “method” (String); we can set this method to be either “POST” or “GET”, ours will default to “POST”. Usually web services will tell you which method to use in order to access their information. The second argument is “arguments” (Object); This will hold any parameters that you would need to make your service call. Again, web services will tell you if you need to send any additional information with your call, ours defaults to null.

package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends DispatchExtension
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

		public function WebService($servicePath:String)
		{
			request = new URLRequest($servicePath);
		}

		/**
		 * Makes the web service call
		 * @param $method either POST or GET. POST is set as default
		 * @param $arguments any parameters or variables that you need to make with your web service call
		 *
		 */
		public function call($method:String="POST", $arguments:Object=null):void
		{
			variables = new URLVariables();
			if($arguments != null){
				for (var i:* in $arguments){
					variables[i] = $arguments[i];
				}
				request.data = variables;
			}
			request.method = $method.toUpperCase();
			loader = new URLLoader(request);
			loader.addEventListener(Event.COMPLETE, handleRequestLoaded);
			loader.addEventListener(IOErrorEvent.IO_ERROR, handleRequestError);
			loader.load(request);
		}
    }
}

Now let me explain what’s going on up there. Basically we set up our URLVariables by recursing through the “$arguments” parameter if it’s not set to null and set it to the “data” proerty or our URLRequest. Then we specify the call method by setting the URLRequest’s “method” property to the “$method” parameter. We then continue to set up out URLLoader instance by adding the COMPLETE and IOErrorEvent event listeners. Finally we make the service call by executing the URLLoader’s call method and passing it the URLRequest.

Now we create the handlers for our event listeners:

package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

		public function WebService($servicePath:String)
		{
			request = new URLRequest($servicePath);
		}

		/**
		 * Makes the web service call
		 * @param $method either POST or GET. POST is set as default
		 * @param $arguments any parameters or variables that you need to make with your web service call
		 *
		 */
		public function call($method:String="POST", $arguments:Object=null):void
		{
			variables = new URLVariables();
			if($arguments != null){
				for (var i:* in $arguments){
					variables[i] = $arguments[i];
				}
				request.data = variables;
			}
			request.method = $method.toUpperCase();
			loader = new URLLoader(request);
			loader.addEventListener(Event.COMPLETE, handleRequestLoaded);
			loader.addEventListener(IOErrorEvent.IO_ERROR, handleRequestError);
			loader.load(request);
		}

		private function handleRequestLoaded($event:Event):void
		{
			data = $event.target.data;
			this.dispatchEvent(new Event("RESULT", false));
		}

		private function handleRequestError($event:IOErrorEvent):void
		{
			data = {};
			this.dispatchEvent(new Event("FAULT", false));
		}

	}
}

In the handlers, if the call is successfull, it sets the data variable to the data that the web service returns and fires a “RESULT” event. If the call is unsuccessful, set the data to an empty object and fires teh “FAULT” event. Now we’ll add getter and setter functions so that we may be able to dynamically set the web service path whenever we want.

package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

		public function WebService($servicePath:String)
		{
			request = new URLRequest($servicePath);
		}

		/**
		 * Makes the web service call
		 * @param $method either POST or GET. POST is set as default
		 * @param $arguments any parameters or variables that you need to make with your web service call
		 *
		 */
		public function call($method:String="POST", $arguments:Object=null):void
		{
			variables = new URLVariables();
			if($arguments != null){
				for (var i:* in $arguments){
					variables[i] = $arguments[i];
				}
				request.data = variables;
			}
			request.method = $method.toUpperCase();
			loader = new URLLoader(request);
			loader.addEventListener(Event.COMPLETE, handleRequestLoaded);
			loader.addEventListener(IOErrorEvent.IO_ERROR, handleRequestError);
			loader.load(request);
		}

		private function handleRequestLoaded($event:Event):void
		{
			data = $event.target.data;
			this.dispatchEvent(new Event("RESULT", false));
		}

		private function handleRequestError($event:IOErrorEvent):void
		{
			data = {};
			this.dispatchEvent(new Event("FAULT", false));
		}

		public function set servicePath($path:String):void
		{
			request = new URLRequest($path);
		}

		public function get servicePath():String
		{
			return request.url;
		}

	}
}

That’s basically all we need for our base WebService calss! Was it easy? Hope so 😉 lets fire it up by using it in a simple Twitter API!

Using the newly created WebService class to make a simple Twitter API

Create a new Actionscript file, name it “Twitter” and save it to the webservices folder. We want this class to fire events also, so lets start off by extending the EventDispatcher class. Remember also to import the flash events package and our WebService class too:

package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{
    }

}

Cool, let’s create a static private var that will hold an HTTP Status method provided by the online Twitter API. You can find all the Twiiter methods over at the Twitter RESR API Docunebtation here: http://apiwiki.twitter.com/REST+API+Documentation#HTTPStatusCodes. We’re going to use only one for now. The call to get all the Tweets on the public timeline.

We’re also going to need a private variable to represent our WebService class and a public array variable which will hold the parsed xml returned byt Twitter:

package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

        }
    }
}

Ok, now let’s set up our constructor. In the constructor we will set up our instance of the WebService class. Remeber that the WebService class needs to accept a service path in it’s constructor, so we’ll pass it our PUBLIC_TIMELINE path as default. We also set the result array to an empty array;

package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

		public function Twitter()
		{
			twitterService = new WebService(Twitter.PUBLIC_TIMELINE);
			twitterService.addEventListener("RESULT", handleResult);
			twitterService.addEventListener("FAULT", handleFault);

			result = [];
		}
    }
}

before we set up the handlers for the events fired from the WebService class, let’s create the method which will make the request for the public timeline to Twitter:

package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

		public function Twitter()
		{
			twitterService = new WebService(Twitter.PUBLIC_TIMELINE);
			twitterService.addEventListener("RESULT", handleResult);
			twitterService.addEventListener("FAULT", handleFault);

			result = [];
		}

		public function requestPublicTimeline():void
		{
			twitterService.servicePath = Twitter.PUBLIC_TIMELINE;
			twitterService.call();
		}
    }
}

What we’re doing is dynamically setting the service path on the Webservices class. Now, you may be asking yourself, “didn’t we just add that path up in the constructor?”, the answer is, yes. Never the less, it is good practice for us to set it up here again, basically because later on when we add new methods, we’ll have to set it to the new service path anyway, so might as well get used to it from now 😉

After the path is set, then we then execute the call method of our WebService class. Nothing is going to happen unless we add our event handlers, so let’s get to it!

package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

		public function Twitter()
		{
			twitterService = new WebService(Twitter.PUBLIC_TIMELINE);
			twitterService.addEventListener("RESULT", handleResult);
			twitterService.addEventListener("FAULT", handleFault);

			result = [];
		}

		public function requestPublicTimeline():void
		{
			twitterService.servicePath = Twitter.PUBLIC_TIMELINE;
			twitterService.call();
		}

		private function handleFault($event:Event):void
		{
			result = [];
			this.dispatchEvent(new Event("FAULT"));
		}

		private function handleResult($event:Event):void
		{
			var myResult:XML = new XML(twitterService.data);
			result = parseResult(myResult);
			this.dispatchEvent(new Event("RESULT"));
		}
    }
}

Nothing too fancy in the fault event handler. Basically, all it does is is just set the reult array to an ampty array and fires a new Fault event. Now, the good stuff is in the result event handler. The response we get from Twitter will be a String in an XML format, so once it arrives, we need make sure flash reads it as XML. Once that is done, we pass it to a method named “parseResult” which we will be seeing in the next step. Essentially, all the “parseResult” method does is take the xml returned from Twitter and parse it into an array of objects, better known as a “Collection”. Once the XML is parsed, it is set as the “result” array and a “RESULT” event is fired. Let’s see that crazy “parseResult” method:

package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

		public function Twitter()
		{
			twitterService = new WebService(Twitter.PUBLIC_TIMELINE);
			twitterService.addEventListener("RESULT", handleResult);
			twitterService.addEventListener("FAULT", handleFault);

			result = [];
		}

		public function requestPublicTimeline():void
		{
			twitterService.servicePath = Twitter.PUBLIC_TIMELINE;
			twitterService.call();
		}

		private function handleResult($event:Event):void
		{
			var myResult:XML = new XML(twitterService.data);
			result = parseResult(myResult);
			this.dispatchEvent(new Event("RESULT"));
		}

		private function handleFault($event:Event):void
		{
			result = [];
			this.dispatchEvent(new Event("FAULT"));
		}

		private function parseResult($result:XML):Array
		{
			var objectArray:Array = [];
			var xml:XML = $result
			var xmllist:XMLList = xml.children();

			for(var a:Number = 0; a < xmllist.length(); a++){
				var object:Object = {};
				for(var b:Number = 0; b< xmllist[a].children().length(); b++){
					var item:XML = xmllist[a].children()[b];
					if(item.localName().toString() == "user"){
						var userObject:Object = {};
						for(var c:Number = 0; c < item.children().length(); c++){
							userObject[item.children()[c].localName().toString()] = item.children()[c].toString()
						}
						object[item.localName()] = userObject;
					}else{
						object[item.localName()] = item.toString();
					}
				}
				objectArray.push(object);
			}
			return objectArray;
		}
	}
}

With the “parseResult” method, each node in the XML will be parsed into an object containing the following values:

id
in_reply_to_status_id
source
created_at
user
  -- name
  -- url
  -- screen_name
  -- description
  -- followers_count
  -- id
  -- protected
  -- profile_image_url
  -- location
in_reply_to_user_id
text
favorited
truncated

I think it’s time we test ride this puppy! Let’s get to it!

Creating a Twitter Application with our newly created Twitter API

Crack open an fla file and set the stage dimensions to 310 by 400. We’ll start off by creating a container for each Tweet. The container movieclip will hold the users profile image and a textfield to display the user’s tweet. Each of a users profile image is 48 x 48, so create a square on the stage 48 x 48 and create a static text field which says “LOADING” and place it right at the center of the box. Now convert the whole thing into a MovieClip and instance name it “imageContainer”;

Now create a dynamic text field with the following properties:

Set the text field right next to the imageContainer and create a MovieClip out of the whole thing. So essentially at this point you should have ONE MovieClip on the stage. This movie clip should contain an 48 x 48 MovieClip instance named “imageContainer” and a dynamic text field instance named “twitTxt”

Drag another 5 instances of the newly created movie clip from the library and drop them on the stage and align them one under the other like shown below and instance name then item0, item1, item2, item3, item4 and item5:

That’s it for the timeline stuff. Select the first frame and open up your actions panel. The first thing we need to do is import our Twitter class;

import com.reyco1.webservice.Twitter

Now we create an instance of our Twitter API and have it listen to the “RESULT” event:

import com.reyco1.webservice.Twitter

var twitter:Twitter = new Twitter();
twitter.addEventListener("RESULT", handleResult);

Let’s set up the handler for the result event. In the handler we pass the first 6 of the objects in the result array to a “populateItem” method along with its index. Let’s take a look at the event handler and then we’ll see what that “populateItem” method looks like.

import com.reyco1.webservice.Twitter

var twitter:Twitter = new Twitter();
twitter.addEventListener("RESULT", handleResult);

function handleResult($event:Event):void
{
	var result:Array = twitter.result;
	for(var a:Number = 0; a < 6; a++){
		populateItem(a, result[a]);
	}
}

Below we’ve added the “populateItem” method. Basically, this method adds the tweet to the text field in the item on stage along with adding the image to it’s imageContainer.

import com.reyco1.webservice.Twitter

var twitter:Twitter = new Twitter();
twitter.addEventListener("RESULT", handleResult);

function handleResult($event:Event):void
{
	var result:Array = twitter.result;
	for(var a:Number = 0; a < 6; a++){
		populateItem(a, result[a]);
	}
}

function populateItem($itemIndex:Number, $twitObject:Object):void
{
	this["item"+$itemIndex].twitText.text = $twitObject.text;
	var imageLoader:Loader = new Loader();
	this["item"+$itemIndex].imageContainer.addChild(imageLoader);
	var imagePath:URLRequest = new URLRequest($twitObject.user.profile_image_url);
	imageLoader.load(imagePath);
}

Now all we have to do is call the “requestPublicTimeline” method of our Twitter API and we’re DONE!!

import com.reyco1.webservice.Twitter

var twitter:Twitter = new Twitter();
twitter.addEventListener("RESULT", handleResult);

function handleResult($event:Event):void
{
	var result:Array = twitter.result;
	for(var a:Number = 0; a < 6; a++){
		populateItem(a, result[a]);
	}
}

function populateItem($itemIndex:Number, $twitObject:Object):void
{
	this["item"+$itemIndex].twitText.text = $twitObject.text;
	var imageLoader:Loader = new Loader();
	this["item"+$itemIndex].imageContainer.addChild(imageLoader);
	var imagePath:URLRequest = new URLRequest($twitObject.user.profile_image_url);
	imageLoader.load(imagePath);
}

twitter.requestPublicTimeline();

Publishing your file as an AIR application

Publishing a flash file as an AIR application cannot be any easier:

  • Download the runtime installation file.
  • Double-click the runtime installation file.
  • In the installation window, follow the prompts to complete the installation.

Once installed, follow these steps:

  • Open your Flash FLA file.
  • If you’re opening a new Flash File (ActionScript 3.0), save it. If you don’t save it, a warning appears when you do the next step.
  • Select Commands > AIR – Application And Installer Settings. An alert box appears, asking if you want to convert the file to Adobe AIR publish settings.
  • Click OK to convert the FLA file to Adobe AIR publish settings. The AIR – Application And Installer Settings dialog box appears.

Simply publish your and all should work! I hope you have enjoyed this article and have been able to learn lots of stuff! In the next few tutorials, I’ll be adding more complex methods to our Twitter API and Hopefully at the end of the series, we’ll have a fully working Twitter Application!

Comments (7)

  1. oops!!
    demo send box error..
    plz… check the site..

  2. Same here….. if I run it in the flash IDE, it works fine, if on a webserver, it hangs. Is it the cross domain policy?

  3. Hey

    Yo, the guys at twitter changed the crossdomain file. ( http://groups.google.com/group/twitter-development-talk/browse_thread/thread/8d09970f449abc70 )
    You can no longer load stuff with flash from a different domain than twitter.com.
    Workaround: Load the xml with a php file on your webserver and print it out. Then load the php file with flash.

    greetings
    rafael

  4. Thanks for the work around Rafael.

  5. Noticed a typo in one of the code samples:

    package com.helix.webservice

    should be

    package com.reyco1.webservice

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>