PureMVC Tutorial – Flex, PureMVC, Jabber and XIFF 3: Conclusion, Demo & Downloads

Introduction
Part 1 – Frameworks
Part 2 – Directory structure
Part 3 – Application and ApplicationFacade
Part 4 – Notifications, Commands & Use Cases
Part 5 – Model & Proxy
Part 6 – The Application View & Mediator
Part 7 – The Login View & Mediator
Part 8 – The Roster View & Mediator
Part 9 – The Chat View & Mediator
Conclusion, Demo & Downloads

Here is a working demo of the application!

If you get a ‘Security Error’ when trying to connect, it means that the particular Jabber server you are connecting to hasn’t implemented its crossdomain.xml file correctly (many servers leave out the content-type which means that the latest versions of Flash Player will refuse to load it).  Anyway, this security policy is ignored when running from your local machine so even if you have trouble using it here it will work fine when you compile it yourself.

You can download the full source code for the project here.

If you spot any mistakes, want to have a lively discussion or just want to say how much you enjoyed this tutorial please feel free to leave comments on the appropriate page!

My thanks go out to Jive Software for XIFF, Cliff Hall for PureMVC and all Flashers everywhere 🙂

Dave

PureMVC Tutorial – Flex, PureMVC, Jabber and XIFF 3: Part 7 – The Login View & Mediator

Introduction
Part 1 – Frameworks
Part 2 – Directory structure
Part 3 – Application and ApplicationFacade
Part 4 – Notifications, Commands & Use Cases
Part 5 – Model & Proxy
Part 6 – The Application View & Mediator
Part 7 – The Login View & Mediator
Part 8 – The Roster View & Mediator
Part 9 – The Chat View & Mediator
Conclusion, Demo & Downloads

In this part of the tutorial we’ll create the login bar, and by the end of this section our application will be able to connect to any XMPP server with a username & password!

In the components package create a new MXML file name LoginView.mxml and in the view package create a new mediator called LoginMediator.as using Add->New Mediator… (if you don’t see this menu item be sure you’ve installed the PureMVC FlashDevelop templates from PureMVC: First thoughts & FlashDevelop templates correctly).

As we explained in the previous section add a helper method to the LoginMediator to cast the viewComponent:

   1: private function get loginView():LoginView {
   2:     return viewComponent as LoginView;
   3: }

Note that in order to get the application to compile you’ll need to explicitly import the LoginView using:

   1: import org.davekeen.xiffer.view.components.LoginView;

Now we want to add the LoginView to the display list by including it in Application.mxml (giving it an id of loginView so we can reference it from the mediator).  Add this MXML within the <mx:Application> tag (stick it at the end just before </mx:Application>):

   1: <mx:Canvas left="0" top="0" right="0" bottom="0">
   2:     <view:LoginView id="loginView" />
   3: </mx:Canvas>

Now we need to register our mediator with PureMVC.  Now this point is important to understand as it seems to confuse  – since LoginView is a sub-component of Application we need to register the view within the Application’s mediator.  This can be made into a general rule:

  • If the mediator you are registering heralds the top level component (i.e. Application.mxml) register it in StartupCommand.
  • If the mediator you are registering heralds a child of another component register it in the constructor of that component’s mediator.

Just to confuse things I’d better point out that this rule doesn’t apply in quite the same way if you are dynamically creating and removing mediators, but we’ll save that for another tutorial.

Anyway, the upshot of all that is that we call registerMediator in the constructor of ApplicationMediator:

   1: public function ApplicationMediator(viewComponent:Object) {
   2:     // pass the viewComponent to the superclass where 
   3:     // it will be stored in the inherited viewComponent property
   4:     super(NAME, viewComponent);
   5:     
   6:     facade.registerMediator(new LoginMediator(application.loginView));
   7: }

Here is the code for LoginView.mxml.  As this is a PureMVC tutorial, not a Flex one, I’m not going to go in any detail about how it works, but these are the only things you need to care about:

  • When the user clicks ‘Connect’ it dispatches a LoginViewEvent.LOGIN event containing the username, password and server.
  • When the user clicks ‘DIsconnect’ it dispatches a LoginViewEvent.LOGOUT event.
  • It exposes a showInvalidLoginAlert() method that pops up a ‘Invalid username/password’ window.
   1: <?xml version="1.0" encoding="utf-8"?>
   2: <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" width="100%" height="100%">
   3:     <mx:Script>
   4:         <![CDATA[
   5:         import mx.controls.Alert;
   6:         import org.davekeen.xiffer.events.LoginViewEvent;
   7:         
   8:         /**
   9:          * Dispatch a connect event to the mediator
  10:          */
  11:         private function onConnectClick():void {
  12:             dispatchEvent(new LoginViewEvent(LoginViewEvent.LOGIN, usernameTextInput.text, passwordTextInput.text, serverComboBox.text));
  13:         }
  14:         
  15:         /**
  16:          * Dispatch a disconnect event to the mediator
  17:          */
  18:         private function onDisconnectClick():void {
  19:             dispatchEvent(new LoginViewEvent(LoginViewEvent.LOGOUT));
  20:         }
  21:         
  22:         /**
  23:          * Enable or disable the connect button depending on whether or not the user has entered something into both username and password fields
  24:          */
  25:         private function usernamePasswordChange():void {
  26:             connectButton.enabled = (usernameTextInput.text.length > 0 && passwordTextInput.length > 0);
  27:         }
  28:         
  29:         /**
  30:          * Show an invalid login alert and clear the username and password fields
  31:          */
  32:         public function showInvalidLoginAlert():void {
  33:             // Popup an alert
  34:             Alert.show("Invalid username/password", "Error", Alert.OK, this);
  35:             
  36:             // Clear the input fields and notify the change handler (this will disable the connect button)
  37:             usernameTextInput.text = "";
  38:             passwordTextInput.text = "";
  39:             usernamePasswordChange();
  40:         }
  41:         
  42:         ]]>
  43:     </mx:Script>
  44:     
  45:     <mx:Form defaultButton="{connectButton}" paddingTop="0" paddingBottom="0" paddingLeft="0" paddingRight="0">
  46:         
  47:         <mx:HBox>
  48:             
  49:             <mx:Label text="Username:" selectable="false" fontSize="14" />
  50:             <mx:TextInput id="usernameTextInput" width="100" change="usernamePasswordChange()" />
  51:             
  52:             <mx:Label text="Password:" selectable="false" fontSize="14" />
  53:             <mx:TextInput id="passwordTextInput" change="usernamePasswordChange()" displayAsPassword="true" width="100" />
  54:             
  55:             <mx:Label text="Server:" fontSize="14" />
  56:             <mx:ComboBox id="serverComboBox" editable="true">
  57:                 <mx:ArrayCollection>
  58:                     <mx:String>jabber.se</mx:String>
  59:                     <mx:String>jabber.org</mx:String>
  60:                 </mx:ArrayCollection>
  61:             </mx:ComboBox>
  62:             
  63:             <mx:Button id="connectButton" label="Connect" enabled="false" click="onConnectClick()" />
  64:             <mx:Button id="disconnectButton" label="Disconnect" enabled="false" click="onDisconnectClick()" />
  65:             
  66:         </mx:HBox>
  67:         
  68:     </mx:Form>
  69:     
  70: </mx:Canvas>

We’ll also need to create the custom LoginViewEvent (in the events folder):

   1: package org.davekeen.xiffer.events {
   2:     import flash.events.Event;
   3:     
   4:     /**
   5:     * Events passed between the login view component and its mediator
   6:     * 
   7:     * @author Dave Keen
   8:     */
   9:     public class LoginViewEvent extends Event {
  10:         
  11:         public static const REGISTER:String = "login_view_register";
  12:         public static const LOGIN:String = "login_view_login";
  13:         public static const LOGOUT:String = "login_view_logout";
  14:         
  15:         private var username:String;
  16:         private var password:String;
  17:         private var server:String;
  18:         
  19:         public function LoginViewEvent(type:String, username:String = null, password:String = null, server:String = null, bubbles:Boolean = false, cancelable:Boolean = false) { 
  20:             super(type, bubbles, cancelable);
  21:             
  22:             this.username = username;
  23:             this.password = password;
  24:             this.server = server;
  25:         }
  26:         
  27:         public function getUsername():String {
  28:             return username;
  29:         }
  30:         
  31:         public function getPassword():String {
  32:             return password;
  33:         }
  34:         
  35:         public function getServer():String {
  36:             return server;
  37:         }
  38:         
  39:         public override function clone():Event { 
  40:             return new LoginViewEvent(type, username, password, server, bubbles, cancelable);
  41:         }
  42:         
  43:     }
  44:     
  45: }

Now we’re ready to start the interesting bit – implementing our LoginMediator.  These are the steps I take when implementing a mediator:

  1. Add listeners for all the events dispatched from the view component to the constructor and create event listener methods for them.
  2. Identify which notifications this mediator is interested in and add them to the listNotificationInterests method’s array and the handleNotification method’s switch statement.
  3. Fill in the event listener methods and switch statement clauses.

Now we’ll do each of these steps in turn for our LoginMediator.

1. Add listeners

Our view component dispatches LoginViewEvent.LOGIN and LoginViewEvent.LOGOUT event, so we’ll add our listeners in the constructor and add two empty event listener methods:

   1: public function LoginMediator(viewComponent:Object) {
   2:     // pass the viewComponent to the superclass where 
   3:     // it will be stored in the inherited viewComponent property
   4:     super(NAME, viewComponent);
   5:     
   6:     loginView.addEventListener(LoginViewEvent.LOGIN, onConnectClick);
   7:     loginView.addEventListener(LoginViewEvent.LOGOUT, onDisconnectClick);
   8: }
   9:  
  10: /**
  11:  * The connect button was clicked in the view
  12:  * 
  13:  * @param    loginViewEvent
  14:  */
  15: private function onConnectClick(loginViewEvent:LoginViewEvent):void { }
  16:  
  17: /**
  18:  * The disconnect button was clicked in the view
  19:  * 
  20:  * @param    loginViewEvent
  21:  */
  22: private function onDisconnectClick(loginViewEvent:LoginViewEvent):void { }

2. Add notifications

The login view is interest in knowing if a client is connected or not (so that it can enable/disable the ‘Connect’ & ‘Disconnect’ buttons accordingly.  It is also interested in knowing if a login attempt was invalid so that it can popup the ‘Invalid username/password’ alert.  This translates into listening for ApplicationFacade.VALID_LOGIN, ApplicationFacade.INVALID_LOGIN and ApplicationFacade.DISCONNECT.

Firstly we’ll add these to the listNotificationInterests method:

   1: override public function listNotificationInterests():Array {
   2:     return [
   3:             ApplicationFacade.VALID_LOGIN,
   4:             ApplicationFacade.INVALID_LOGIN,
   5:             ApplicationFacade.DISCONNECT
   6:             ];
   7: }

Now we’ll add empty clauses for each notification in the handleNotification method’s switch statement:

   1: override public function handleNotification(note:INotification):void {
   2:     switch (note.getName()) {
   3:         case ApplicationFacade.VALID_LOGIN:
   4:             break;
   5:         case ApplicationFacade.INVALID_LOGIN:
   6:             break;
   7:         case ApplicationFacade.DISCONNECT:
   8:             break;
   9:         default:
  10:             break;        
  11:     }
  12: }

3. Fill in the event listeners and switch clauses

Let do the switch clauses first.  Its all simple stuff – in the event of a valid login we want to enable the ‘Disconnect’ button and disable the ‘Connect’ button, in the event of a disconnect we want to enable the ‘Connect’ button and disable the ‘Disconnect’ button, and in the event of an invalid login we want to popup the ‘Invalid username/password’ alert box:

   1: override public function handleNotification(note:INotification):void {
   2:     switch (note.getName()) {
   3:         case ApplicationFacade.VALID_LOGIN:
   4:             loginView.connectButton.enabled = false;
   5:             loginView.disconnectButton.enabled = true;
   6:             break;
   7:         case ApplicationFacade.INVALID_LOGIN:
   8:             loginView.showInvalidLoginAlert();
   9:             break;
  10:         case ApplicationFacade.DISCONNECT:
  11:             loginView.connectButton.enabled = true;
  12:             loginView.disconnectButton.enabled = false;
  13:             break;
  14:         default:
  15:             break;        
  16:     }
  17: }

And now lets fill in our event listeners.  Again, these are very simple – all they do is send the appropriate notification which will then get auto-mapped to the appropriate command – in this case either LoginCommand or LogoutCommand.

   1: private function onConnectClick(loginViewEvent:LoginViewEvent):void {
   2:     sendNotification(ApplicationFacade.LOGIN, loginViewEvent);
   3: }
   4:  
   5: private function onDisconnectClick(loginViewEvent:LoginViewEvent):void {
   6:     sendNotification(ApplicationFacade.LOGOUT, loginViewEvent);
   7: }

Notice that for the parameter of the notifications I am just passing the same LoginViewEvent we received from the view component.  It could be argued that this breaks encapsulation as the commands shouldn’t really know anything about events.  However, after coding a few PureMVC projects I’ve noticed that its very common for the event you receive from the view to contain the same bits of information needed by the relevant command – in this case LoginCommand needs to know the username, password and server which is exactly the information contained in a LoginViewEvent.  Because of this I don’t really see the need to create an extra object, and when passing an event as notification parameter I just think of it as a value object instead of an event, but you are certainly justified in taking another view on this.

Now that we have the mediator calling the LoginCommand and LogoutCommand we’d better fill these in.  Commands can do various different things, but a very common pattern for commands, and what we’ll be using here, is:

  1. Retrieve the proxy we want to do something with using retrieveProxy.
  2. Call a method on that proxy, possibly with parameters ripped out of the notification parameter.

With that in mind we can very simply implement LoginCommand.as:

   1: override public function execute(note:INotification):void {
   2:     var loginViewEvent:LoginViewEvent = note.getBody() as LoginViewEvent;
   3:     var xmppProxy:XMPPProxy = facade.retrieveProxy(XMPPProxy.NAME) as XMPPProxy;
   4:     
   5:     xmppProxy.connect(loginViewEvent.getUsername(), loginViewEvent.getPassword(), loginViewEvent.getServer());
   6: }

… and LogoutCommand.as:

   1: override public function execute(note:INotification):void {
   2:     var xmppProxy:XMPPProxy = facade.retrieveProxy(XMPPProxy.NAME) as XMPPProxy;
   3:     
   4:     xmppProxy.disconnect();
   5: }

Guess what?  We have a working application!  Compile the application and play about with it – you’ll be able to log in and out of Jabber servers to your heart’s content 🙂  If you download a proper jabber client (http://www.jabber.org/clients has a big list of clients for various platforms) you’ll be able to see your user coming on and offline as you click ‘Connect’ and ‘Disconnect’.

Be proud!  All that’s left for us to do now is to create a buddy list (called the Roster in Jabber parlance) and the chat windows themselves.  If you’ve come this far maybe you’d like to have a go yourself without reading further.  If not, read on – Roster view here we come.

PureMVC Tutorial – Flex, PureMVC, Jabber and XIFF 3: Part 6 – The Application View & Mediator

Introduction
Part 1 – Frameworks
Part 2 – Directory structure
Part 3 – Application and ApplicationFacade
Part 4 – Notifications, Commands & Use Cases
Part 5 – Model & Proxy
Part 6 – The Application View & Mediator
Part 7 – The Login View & Mediator
Part 8 – The Roster View & Mediator
Part 9 – The Chat View & Mediator
Conclusion, Demo & Downloads

Congratulations on getting this far – we’re getting closer to our working Jabber client 🙂  Before we get going with our first top-level mediator we’ll talk about views in a little more detail.  In PureMVC a view consists of:

  • One or more view components.  This is the thing that the user actually sees and interacts with – in Flash it will be a Sprite or MovieClip, and in Flex it will be an MXML file (or an AS file extending a flex component).
  • A mediator.  This is the PureMVC bit of the view and ‘heralds’ the view components.  Any interaction with the rest of the application is done through the mediator.  Note that although the mediator knows about the existence of the view components, the view components don’t know about existence of the mediator!
  • Events.  Although not strictly a part of the view, I find it helpful to think of events within the same bundle.  View components communicate with their mediator via events and its common to create custom events for various interactions within the view component.  For example, when we come to the Login view we’re going to be dispatching a custom LoginViewEvent to the LoginMediator when the user clicks on the ‘connect’ button.

PureMVC makes no assumptions about the way that you structure the tree of views and mediators, apart from requiring that there is one top-level mediator.  In the words of our great leader, Cliff Hall (the creator of PureMVC):

It is up to you as a developer to determine the granularity of the View Component that is handled a given Mediator. If a Mediator becomes too bloated dealing with all the children of its View Component simply create a Mediator for the more complicated child. For instance, if the child components of the Application instance are complicated enough to justify their own Mediators, then the ApplicationMediator typically creates and registers the appropriate Mediator for each of those children.

This means that you start off with one mediator for your application entry point (i.e. Application.mxml) and then add extra mediators as needed.  In general you tend to have one mediator per visual ‘section’ of your application, but this is by no means enforced.

Create our ApplicationMediator.as in the view folder with Add->New Mediator… (if you don’t see this menu item be sure you’ve installed the PureMVC FlashDevelop templates from PureMVC: First thoughts & FlashDevelop templates correctly).

At this point its a good idea to add in a helper method to allow you to retrieve the view component that this mediator is looking after (in this case Application.mxml).  To do this add the following private getter:

   1: private function get application():Application {
   2:     return viewComponent as Application;
   3: }

This saves having to constantly case viewComponent to the correct class every time we use it.  Unfortunately I couldn’t find a way to get a FlashDevelop template to downcase the class name so we can’t generate this function within the template, but you’ll soon get into the habit of typing it each time you create a new mediator.

Finally we need to add our new mediator to PureMVC using the registerMediator command in StartupCommand.  The execute method in this command should now read:

   1: override public function execute(notification:INotification):void {
   2:     facade.registerProxy(new XMPPProxy());
   3:     
   4:     facade.registerMediator(new ApplicationMediator(notification.getBody() as Application));
   5: }

Notice the argument passed to the Mediator – this is the view component that the mediator is supposed to look after (in this case Application.mxml).  notification.getBody() retrieves the argument passed along with the notification, and since we sent the notification (in Application.mxml) using facade.sendNotification(ApplicationFacade.STARTUP, this) that argument will be the Application object itself!

Now we’ll create the Login view and mediator.

PureMVC Tutorial – Flex, PureMVC, Jabber and XIFF 3: Part 5 – Model & Proxy

Introduction
Part 1 – Frameworks
Part 2 – Directory structure
Part 3 – Application and ApplicationFacade
Part 4 – Notifications, Commands & Use Cases
Part 5 – Model & Proxy
Part 6 – The Application View & Mediator
Part 7 – The Login View & Mediator
Part 8 – The Roster View & Mediator
Part 9 – The Chat View & Mediator
Conclusion, Demo & Downloads

The model is the ‘data’ of our application and the proxy is our interface onto that data.  For our particular application the model itself is basically our Jabber connection object, so the Proxy will encapsulate that and expose an interface with the following methods:

  • connect(username:String, password:String, server:String):void
  • disconnect():void
  • sendMessage(message:Message):void
  • getRosterDataProvider():ArrayCollection

As you can see, the interface is very similar to our use case and commands, and this in no coincidence; each command is going to call the relevant method in its execute method.

Create a Proxy in the model folder called XMPPProxy.as using Add->New Proxy… (if you don’t see this menu item be sure you’ve installed the PureMVC FlashDevelop templates from PureMVC: First thoughts & FlashDevelop templates correctly).

I’m just going to include the Proxy code here without much explanation as it doesn’t really do anything overly complicated (all of the hard stuff has been done for us in the XIFF library 🙂 )  Have a read through the comments of each method and everything should be clear.

   1: /*
   2: Proxy - PureMVC
   3: */
   4: package org.davekeen.xiffer.model {
   5:     import flash.events.Event;
   6:     import flash.system.Security;
   7:     import org.davekeen.xiffer.ApplicationFacade;
   8:     import org.jivesoftware.xiff.core.JID;
   9:     import org.jivesoftware.xiff.core.XMPPSocketConnection;
  10:     import org.jivesoftware.xiff.data.Message;
  11:     import org.jivesoftware.xiff.data.Presence;
  12:     import org.jivesoftware.xiff.events.*
  13:     import org.jivesoftware.xiff.im.Roster;
  14:     import org.puremvc.as3.interfaces.IProxy;
  15:     import org.puremvc.as3.patterns.proxy.Proxy;
  16:     import mx.collections.ArrayCollection;
  17:  
  18:     /**
  19:      * Proxy to XMPP server
  20:      */
  21:     public class XMPPProxy extends Proxy implements IProxy {
  22:         
  23:         public static const NAME:String = "XMPPProxy";
  24:         
  25:         private var xmppSocketConnection:XMPPSocketConnection;
  26:         private var roster:Roster;
  27:  
  28:         public function XMPPProxy(data:Object = null) {
  29:             super(NAME, data);
  30:             
  31:             setupConnection();
  32:             configureListeners();
  33:         }
  34:         
  35:         /**
  36:          * Create the required XMPP objects and do any configuration on them that we might require
  37:          */
  38:         private function setupConnection():void {
  39:             xmppSocketConnection = new XMPPSocketConnection();
  40:             
  41:             roster = new Roster();
  42:             roster.connection = xmppSocketConnection;
  43:         }
  44:         
  45:         private function configureListeners():void {
  46:             // Add event listeners related to the connection
  47:             xmppSocketConnection.addEventListener(LoginEvent.LOGIN, onLogin);
  48:             xmppSocketConnection.addEventListener(XIFFErrorEvent.XIFF_ERROR, onXiffError);
  49:             xmppSocketConnection.addEventListener(DisconnectionEvent.DISCONNECT, onDisconnect);
  50:             
  51:             // Add event listeners related to messages
  52:             xmppSocketConnection.addEventListener(MessageEvent.MESSAGE, onMessage);
  53:             
  54:         }
  55:         
  56:         /**
  57:          * Attempt to connect to a XMPP server
  58:          * 
  59:          * @param    username
  60:          * @param    password
  61:          * @param    server
  62:          */
  63:         public function connect(username:String, password:String, server:String):void {
  64:             // Attempt to load a crossdomain permissions file
  65:             Security.loadPolicyFile(server + "/crossdomain.xml");
  66:             
  67:             // Connect using standard profile
  68:             xmppSocketConnection.username = username;
  69:             xmppSocketConnection.password = password;
  70:             xmppSocketConnection.server = server;
  71:             xmppSocketConnection.connect("standard");
  72:         }
  73:         
  74:         /**
  75:          * Disconnect from a XMPP server.  If not currently connected this will have no effect.
  76:          * 
  77:          */
  78:         public function disconnect():void {
  79:             xmppSocketConnection.disconnect();
  80:         }
  81:         
  82:         /**
  83:          * Return the roster as a data provider
  84:          * 
  85:          * @return
  86:          */
  87:         public function getRosterDataProvider():ArrayCollection {
  88:             return roster;
  89:         }
  90:         
  91:         /**
  92:          * Send a message to the server
  93:          * 
  94:          * @param    message
  95:          */
  96:         public function sendMessage(message:Message):void {
  97:             xmppSocketConnection.send(message);
  98:         }
  99:         
 100:         /**
 101:          * The user has successfully logged on to the XMPP server
 102:          * 
 103:          * @param    connectionSuccessEvent
 104:          */
 105:         private function onLogin(loginEvent:LoginEvent):void {
 106:             roster.setPresence(Presence.SHOW_CHAT, "", 0);
 107:             
 108:             sendNotification(ApplicationFacade.VALID_LOGIN);
 109:         }
 110:         
 111:         /**
 112:          * There has been a Jabber error - most likely an incorrect username/password error
 113:          * 
 114:          * @param    xiffErrorEvent
 115:          */
 116:         private function onXiffError(xiffErrorEvent:XIFFErrorEvent):void {
 117:             if (xiffErrorEvent.errorCode == 400)
 118:                 sendNotification(ApplicationFacade.INVALID_LOGIN);
 119:             
 120:         }
 121:         
 122:         /**
 123:          * The user has disconnected from the XMPP server
 124:          * 
 125:          * @param    disconnectionEvent
 126:          */
 127:         private function onDisconnect(disconnectionEvent:DisconnectionEvent):void {
 128:             sendNotification(ApplicationFacade.DISCONNECT);
 129:         }
 130:         
 131:         /**
 132:          * Received a message from the server
 133:          * 
 134:          * @param    messageEvent
 135:          */
 136:         private function onMessage(messageEvent:MessageEvent):void {
 137:             sendNotification(ApplicationFacade.RECEIVE_MESSAGE, messageEvent.data);
 138:         }
 139:         
 140:     }
 141: }

One thing you will notice is that the proxy dispatches another few notifications that we haven’t included in our ApplicationFacade:

  • VALID_LOGIN
  • INVALID_LOGIN
  • RECEIVE_MESSAGE
  • DISCONNECT

So lets go back to our ApplicationFacade and add them in:

   1: public static const VALID_LOGIN:String = "valid_login";
   2: public static const INVALID_LOGIN:String = "invalid_login";
   3: public static const DISCONNECT:String = "disconnect";
   4: public static const RECEIVE_MESSAGE:String = "receive_message";

The final thing we need to do is register our new Proxy with PureMVC.  We do this in StartupCommand.as using the registerProxy method:

   1: override public function execute(notification:INotification):void {
   2:     facade.registerProxy(new XMPPProxy());
   3: }

Its quite easy to forget to do this and end up with all kinds of strange errors, so be sure to remember to register any proxies you create.

We’ve finally got the bones of our application up and running so now its on to the views and mediators!

PureMVC Tutorial – Flex, PureMVC, Jabber and XIFF 3: Part 1 – Frameworks

Introduction
Part 1 – Frameworks
Part 2 – Directory structure
Part 3 – Application and ApplicationFacade
Part 4 – Notifications, Commands & Use Cases
Part 5 – Model & Proxy
Part 6 – The Application View & Mediator
Part 7 – The Login View & Mediator
Part 8 – The Roster View & Mediator
Part 9 – The Chat View & Mediator
Conclusion, Demo & Downloads

Now that we’ve got a basic FlashDevelop project setup we can install the frameworks we’ll be using.

PureMVC

Download the PureMVC framework from here. Put this into a directory somewhere on your machine and add this to the FlashDevelop project classpath like so:

  1. Right click on ‘XIFFer’ and select Properties…
  2. Go to the Classpaths tab
  3. Click Add Classpath… and in the directory selector choose the PureMVC src folder.

XIFF

XIFF is an excellent Actionscript XMPP (Jabber) framework that we’ll be using to take care of the internals of Jabber communication. Its been around since Actionscript 2 but has recently been ported to Actionscript 3 (albeit in beta form). Download it here, put it into a directory and add the classpath as above.

As XIFF is currently in beta you’ll need to make a small change in order to get it to play nice with ejabberd – a common Jabber server written in Erlang. Research tells me that this is actually a problem with ejabberd’s implementation rather than XIFF’s, but whatever the issue might be this will fix it 🙂

  1. Open org.jivesoftware.xiff.core.XMPPSocketConnection
  2. Goto line 108
  3. Remove the version=”1.0″ / so that the line now reads:

openingStreamTag = “<?xml version=”1.0″?><stream:stream to=”” + server + “” xmlns=”jabber:client” xmlns:stream=”http://etherx.jabber.org/streams”>”;

Now we’re all sorted with the bits we require and its time to set up our directories and packages.

PureMVC Tutorial – Flex, PureMVC, Jabber and XIFF 3: Introduction

Introduction
Part 1 – Frameworks
Part 2 – Directory structure
Part 3 – Application and ApplicationFacade
Part 4 – Notifications, Commands & Use Cases
Part 5 – Model & Proxy
Part 6 – The Application View & Mediator
Part 7 – The Login View & Mediator
Part 8 – The Roster View & Mediator
Part 9 – The Chat View & Mediator
Conclusion, Demo & Downloads

I like Actionscript 3, I like Flex, I like Flash and I especially like PureMVC. In fact, I like them so much that its about time I wrote a fully fledged tutorial to help the rest of you like them as much as I do. The code we are going to recreate here started life as a technical test for a job I was interested in. Unfortunately it ended up not being for me, but the code itself lends itself excellently to demonstrating how to use PureMVC as well as creating a decent application along the way. As you have probably guessed from the title we’ll be creating a simple but fully functional Jabber chat client. Its not exactly going to be feature rich, but by the end of this tutorial your application will allow you to login, logout, show your buddy list and have one-to-one chats with your friends. Chatrooms, invites and all the other bits of Jabber goodness are left as an exercise for the reader 🙂

Before we start I should also humbly point out that there are many ways to program using PureMVC – what I’ve presented in this tutorial is the way that I like to do it, and the way that makes the most sense to me. However there are many equally valid ways to develop within a framework and you shouldn’t take my word as gospel, but work in whichever way you find suits you best.

Note that I’ve programmed the client using Flex merely because its quicker to create popup windows, form elements, etc in Flex than Flash. However, the bones of the app are valid in both so even if you don’t use Flex often you should still be able to get something out of this.

Before we get going, you’ll need the following:

Now we have the tools, its time to setup our project. We’re going to create an empty FlashDevelop project ready to receive our code. Open FlashDevelop and select Project->New Project. Choose Flex 3 project from the list, name the project ‘XIFFer’, check ‘Create directory for project’ and finally click OK to create the new project.

Finally we need to set the dimensions of our Flex application

  1. Right click on ‘XIFFer’ and select Properties…
  2. Set the dimensions to 760 x 400 px
  3. Click OK

We’ve got everything we need to get going! Continue onto part 1…