Question

I see that Google Analytics (as part of their new and recommended Universal Analytics solution) provides a simple RESTful interface called the Measurement Protocol for collecting analytics from a variety of platforms or applications.

How do you use this interface from a Flash/AS3 app? I'm creating a payload of url-formatted parameters according to the docs, but I'm getting a SecurityError because crossdomain.xml is not hosted at http://www.google-analytics.com/crossdomain.xml when making the URL request:

[SecurityErrorEvent type="securityError" bubbles=false cancelable=false eventPhase=2 text="Error #2048: Security sandbox violation: http://<mysite>/<myapp>.swf cannot load data from http://www.google-analytics.com/collect."]

Using either POST or GET, this call fails in the context of a web browser (though it succeeds in the context of AIR):

// i.e. var payload:String = 'v=1&t=event&ec=category&ea=action'+
//   '&el=label&tid=UA-xxxxxxxx-x&cid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

var req:URLRequest = new URLRequest('http://www.google-analytics.com/collect');
req.method = URLRequestMethod.POST;
req.data = payload;
var urlLoader:URLLoader = new URLLoader();
urlLoader.load(req);

I need these analytics to work from either AIR or the Flash Player (on a web page).

Was it helpful?

Solution

As is noted in the URLRequest docs, cross-site scripting (xss) restrictions require a crossdomain.xml for POST requests. Since google doesn't host this file, you have to avoid POST. But the measurement protocol doc says it will accept either GET or POST. So you have to use a GET. GET with the above code still throws, but it turns out if you use a Loader instead of a URLLoader (as if you were going to access an image on the web, which is not covered by xss rules), it works:

// i.e. var payload:String = 'v=1&t=event&ec=category&ea=action'+
//   '&el=label&tid=UA-xxxxxxxx-x&cid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

var req:URLRequest = new URLRequest('http://www.google-analytics.com/collect?'+payload);
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(Event.COMPLETE, cleanup);
l.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, cleanup);
l.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, cleanup);
l.load(req);
function cleanup(e:Event):void {
  l.contentLoaderInfo.removeEventListener(Event.COMPLETE, cleanup);
  l.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, cleanup);
  l.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, cleanup);
}

You want the error listeners so that no errors pop-up, and you also need to clean them up to prevent memory leaks.

However, on mobile, I'd still use your original URLLoader code (as it has fewer allocations / events), perhaps using conditional compilation:

// i.e. var payload:String = 'v=1&t=event&ec=category&ea=action'+
//   '&el=label&tid=UA-xxxxxxxx-x&cid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

ENV::AIR {
  var req:URLRequest = new URLRequest('http://www.google-analytics.com/collect');
  req.method = URLRequestMethod.POST;
  req.data = payload;
  var urlLoader:URLLoader = new URLLoader();
  urlLoader.load(req);
}

ENV::WEB {
  var req:URLRequest = new URLRequest('http://www.google-analytics.com/collect?'+payload);
  var l:Loader = new Loader();
  l.contentLoaderInfo.addEventListener(Event.COMPLETE, cleanup);
  l.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, cleanup);
  l.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, cleanup);
  l.load(req);
  function cleanup(e:Event):void {
    l.contentLoaderInfo.removeEventListener(Event.COMPLETE, cleanup);
    l.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, cleanup);
    l.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, cleanup);
  }
}

OTHER TIPS

or you can use a library
Google Universal Analytics for ActionScript 3.0

checkout as3-universal-analytics v0.8

https://github.com/zwetan/as3-universal-analytics/releases/tag/0.8

full support for: Flash Player, AIR, Redtamarin
it just works everywhere or almost everywhere :)

in your case

var config:Configuration = new Configuration();
    config.forcePOST = true;
var tracker:WebTracker = new WebTracker( "UA-12345-67", config );
    tracker.pageview( "/hello/world", "Hello World" );

Jeff has a good answer, but you can improve it by leveraging POST method, not GET, since GET is quite restricted and not good to send a big bunch of data.

var req:URLRequest = new URLRequest(url);
req.method = URLRequestMethod.POST;
req.data = payload;
var l:Loader = new Loader();
l.load(req);

Notice POST request method, it will do the trick

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top