Dec 9, 2010
After uncovering very little info on the subject, I decided to write about how you can use Google Analytics (GA) to monitor usage of an AIR application. Implementation isn’t complex but it does require putting some thought into what you actually want to track since you’re using something meant for websites to monitor a desktop application.
First thing you want to do is to signup for a GA account or create a tracking account that can be used by your AIR app if you’ve already signed up for GA. As part of the tracking account creation, you’ll supply a domain to track which should be related to your application but doesn’t have to be. I’d advise it to be though because it keeps everything cohesive by grouping your app usage metrics with your product site metrics. If you’ve done everything correctly, you’ll now have access to a unique web property ID that maps to your newly created tracking account. This will be very important later on.
Now that you’ve got your tracking account setup, it’s wise to take the time to create your conversion goals. These goals are completely up to you but they need to be defined to allow GA to calculate goal conversion metrics for you. I personally consider the following goals essential to any desktop application:
With the exception of Downloads, all of the above will be tracked via your AIR application through the definition of pageview patterns and application events. When you create your conversion goals you have the option to set the goal’s type as a URL destination which is perfect for generating your pageview patterns because it allows you to specify a goal URL. This URL now becomes the trigger for this metric. So, anytime GA registers a hit on the specified URL it also registers a hit on the corresponding goal. GA allows you to create up to 20 goals split into 4 sets. Here’s what the goal config would look like for a goal that tracks # of registrations via a virtual URL:
Once you’ve setup your conversion goals, you’re pretty much done on the GA side. You can now take the JS embed code that GA generates for you and add it to all the physical pages of your product website before moving on to your AIR application.
Now that the GA side is setup, you can concentrate your efforts on the AIR side of things. First on the AIR todo list is to grab the latest GA library SWC from the gaforflash project site. Once you’ve done that, you want to add the SWC to the library path of your Flex or Flash project. With the SWC on your library path, you can now start wiring your application’s events and user interactions to your pageview patterns that you previously defined.
In a project that I’m currently working on, I chose to tackle this through the use of a value object (VO) that transports the tracking data collected throughout my application to a proxy that handles data submission to GA. The VO includes read/write properties for everything required by the GA methods supplied via the SWC, as well as, static constants for the pageview patterns that I want to track:
package labs.otuome.air.myapp.model.vo.google { /** * Value object representing * application analytics data. * @author Hasan Otuome */ public class AnalyticsData { /////////////////////////////////////////////////////////////////////// // PRIVATE PROPERTIES ////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // PUBLIC PROPERTIES ///////////////////////////////////////////////////////////////////// //------------------- Track Types ---------------------------------// public static const URL:String = 'myapp_pageViewTrack'; public static const EVENT:String = 'myapp_eventTrack'; //------------------- Virtual URLs ---------------------------------// public static const INSTALL:String = '/app/installed'; public static const TRIAL:String = '/app/trial'; public static const PURCHASE:String = '/app/purchase'; public static const REGISTRATION:String = '/app/registered'; public static const HELP:String = '/view/help'; public static const BUG:String = '/report/bug'; /** * The type of data this instance * represents, either AnalyticsData.VIEW * or AnalyticsData.EVENT. */ public var type:String; /** * The data value to associate with this track. */ public var value:Object; //------------------- Event-only ---------------------------------// /** * For event tracks, a custom event category. */ public var category:String; /** * For event tracks, the action related to the event. */ public var action:String; /** * For event tracks, a custom label to * associate with the event. */ public var label:String; ///////////////////////////////////////////////////////////////////// // PUBLIC METHODS ///////////////////////////////////////////////////////////////////// /** * Constructor */ public function AnalyticsData(){} ///////////////////////////////////////////////////////////////////// // OVERRIDES ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // PRIVATE METHODS ///////////////////////////////////////////////////////////////////// } }
And here’s the GA proxy class:
package labs.otuome.air.myapp.model.proxy.google { import com.google.analytics.AnalyticsTracker; import com.google.analytics.GATracker; import flash.display.DisplayObject; import labs.otuome.air.myapp.model.proxy.AbstractProxy; import labs.otuome.air.myapp.model.vo.google.AnalyticsData; import org.puremvc.as3.interfaces.IProxy; /** * The <code>AnalyticsProxy</code> class * provides access to the AnalyticsData, which * contains all of the associated analytics data. * * @author Hasan Otuome (core) */ public class AnalyticsProxy extends AbstractProxy implements IProxy { ///////////////////////////////////////////////////////////////////// // PRIVATE PROPERTIES ///////////////////////////////////////////////////////////////////// /** * The GA tracker object. */ private var _googleAnalyticsTracker:AnalyticsTracker; ///////////////////////////////////////////////////////////////////// // PUBLIC PROPERTIES ///////////////////////////////////////////////////////////////////// /** * Reference to class name for framework caching. */ public static const NAME:String = 'AnalyticsProxy'; /** * The web property ID associated with this application. */ public static const GOOGLE_ANALYTICS_PROPERTY_ID:String = 'UA-12346578-1'; /** * Returns the AnalyticsData. */ public function get track():AnalyticsData{ return getData() as AnalyticsData; } ///////////////////////////////////////////////////////////////////// // PUBLIC METHODS ///////////////////////////////////////////////////////////////////// /** * Constructor * * @param aName * @param aData */ public function AnalyticsProxy( aName:String, aData:Object=null ) { super( aName, aData ); } /** * Starts the GA tracker. * @param displayObject * @param debug */ public function initializeGoogleTracker( displayObject:DisplayObject, debug:Boolean=false ):void { _googleAnalyticsTracker = new GATracker( displayObject, GOOGLE_ANALYTICS_PROPERTY_ID, 'AS3', false ); } /** * Submits the analytics data to * the analytics tracking service. */ public function submitUsageData():void { if (track.type && track.value) { switch (track.type) { case AnalyticsData.URL: _trackPageView(); break; case AnalyticsData.EVENT: _trackEvent(); break; } } } ///////////////////////////////////////////////////////////////////// // OVERRIDES ///////////////////////////////////////////////////////////////////// /** * Proxy registration handler. */ override public function onRegister():void { debugTrace( '[ AnalyticsProxy :: onRegister() ]' ); data = new AnalyticsData(); } //////////////////////////////////////////////////////////////////// // PRIVATE METHODS //////////////////////////////////////////////////////////////////// /** * Submits a virtual page view. */ private function _trackPageView():void { if (_googleAnalyticsTracker) { _googleAnalyticsTracker.trackPageview( track.value.toString() ); // reset the VO track.type = null; track.value = null; track.category = null; track.action = null; track.label = null; } } /** * Submits a virtual event. */ private function _trackEvent():void { if (_googleAnalyticsTracker) { _googleAnalyticsTracker.trackEvent( track.category, track.action, track.label, Number(track.value) ); // reset the VO track.type = null; track.value = null; track.category = null; track.action = null; track.label = null; } } } }
In this scenario, the GA tracker is initialized at application startup and from there, anytime usage data needs to be relayed to GA, the VO tied to the proxy is updated and then the submitUsageData() method is called.
That’s all there is to it. If it seems eerily similar to GA implementations for browser-based SWFs, that’s because it’s pretty much the same except usage from AIR apps is currently unsupported by Google or Adobe. Even still, it’s nice to know that AIR developers don’t have to be left out in the cold completely.
One additional thing to note, it can take up to 24hrs before you’re able to see the analytics data via your GA dashboard but after that period just look under the Content and Goals tabs and you should start seeing data trickle in for all your virtual pageviews and events.