Thursday, January 2, 2014

Using Flash SharedObject to Store User Info Similar to Cookies

Browser cookies are great, but can be limiting and easily lost (or cleared). Some browsers will auto clear cookies when they are closed based on user settings. Recently, while building a Facebook page tab application I ran into an issue where Safari wouldn't keep any cookie data because of iframe security. This ended up being painful and while banging my head on my desk I thought of something. Why not use at tiny flash swf to save cookie data? I know flash is not very popular but it's still installed on most people's browser even if they don't know it.

Here is the ActionScript 3 code to get your very own 10px by 10px flash swf to save cookie data with.
//create a file named FlashCookies.as as add the following code to it:
package
{
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.events.TimerEvent;
 import flash.external.ExternalInterface;
 import flash.net.SharedObject;
 import flash.utils.Timer;
 
 [SWF(width = "10", height = 10, fps = 10)]
 
 public class FlashCookies extends Sprite 
 {
  
  private var cookieName:String;
  private var readyTimer:Timer;
  
  public function Main():void 
  {
   if (stage) init();
   else addEventListener(Event.ADDED_TO_STAGE, init);
  }
  
  private function init(e:Event = null):void 
  {
   removeEventListener(Event.ADDED_TO_STAGE, init);
   
   cookieName = loaderInfo.parameters['c'];
   
   
   if (ExternalInterface.available 
     && cookieName != null 
     && cookieName.length > 0)
   {
    try 
    { 
     // This calls the isContainerReady() method, which in turn calls 
     // the container to see if Flash Player has loaded and the container 
     // is ready to receive calls from the SWF. 
     var containerReady:Boolean = isContainerReady(); 
     if (containerReady) 
     { 
      // If the container is ready, register the SWF's functions. 
      setupCallbacks(); 
     } 
     else 
     { 
      // If the container is not ready, set up a Timer to call the 
      // container at 100ms intervals. Once the container responds that 
      // it's ready, the timer will be stopped. 
      readyTimer = new Timer(100); 
      readyTimer.addEventListener(TimerEvent.TIMER, timerHandler); 
      readyTimer.start(); 
     } 
    } 
    catch(e:Error) { }
    
   }
   else 
   { 
    trace("External interface is not available for this container."); 
   } 
   
  }
  
  private function setupCallbacks():void
  {
   //add callbacks
   ExternalInterface.addCallback('getCookie', getSOCookieData);
   ExternalInterface.addCallback('setCookie', setSOCookieData);
   ExternalInterface.addCallback('clearCookie', clearSOcookieData);
   
   //notify javascript that swf is ready
   ExternalInterface.call('flash_setSWFIsReady');
  }
  
  private function isContainerReady():Boolean 
  { 
   var result:Boolean = ExternalInterface.call("flash_isJSReady"); 
   return result; 
  }
    
  public function getSOCookieData(key:String):String
  {
   var so:SharedObject = SharedObject.getLocal( cookieName, "/" );
   if ( so.data.hasOwnProperty(key) )
    return so.data[key];
   return null;
  }
  
  public function setSOCookieData(key:String, value:String):void
  {
   var so:SharedObject = SharedObject.getLocal( cookieName, "/" );
   so.data[key] = value;
   so.flush();
  }
  
  public function clearSOcookieData():void
  {
   var so:SharedObject = SharedObject.getLocal( cookieName, "/" );
   so.clear();
  }
  
  //Event handlers
  private function timerHandler(event:TimerEvent):void 
  { 
   // Check if the container is now ready. 
   var isReady:Boolean = isContainerReady(); 
   if (isReady) 
   { 
    // If the container has become ready, we don't need to check anymore, 
    // so stop the timer. 
    readyTimer.stop(); 
    readyTimer.removeEventListener(TimerEvent.TIMER, timerHandler);
    readyTimer = null;
    
    // Set up the ActionScript methods that will be available to be 
    // called by the container. 
    setupCallbacks(); 
   } 
  }
 }
 
}

Now you will need to compile this it into a swf with either Adobe Flash or Flash Develop. Along with this generated swf, you'll also need a bit of JavaScript, which isn't much if you use swfobject.
//flash communication methods
var hasFlash = false;
var jsReady = false;
var swfReady = false;

window.flash_isJSReady = function()
{
    return jsReady;
};

window.flash_setSWFIsReady = function()
{
    swfReady = true;
};

function getSwf()
{
    if (navigator.appName.indexOf("Microsoft") !== -1) 
        return window["FlashCookies"]; 
    return document["FlashCookies"]; 
} 

$(document).on('ready', function()
{ 
    //embed flash on page
    var flashvars = {
        //add a custom name for this cookie
        c : 'my_so_cookie'
    };
    var params = {
        allowScriptAccess: "always",
        wmode: "transparent"
    };
    var attributes = {
        id:"FlashCookies"
    };
    swfobject.embedSWF("FlashCookies.swf", "altContent", 10, 10, "9.0.0", null, flashvars, params, attributes);

    // Record that JavaScript is ready to go. 
    jsReady = true; 
} 

And the html would just be a div with the id "altContent" (or whatever you want, just make sure to update the swfobject.embedSWF() call with the matching value.

Once all this is setup, all you need to do is call getSwf().setCookie("email","john@home.net"); and the flash cookie is saved and much harder to clear. The next time that user comes to your site, you can check the cookie getSwf().getCookie("email");.

Nice huh? Since I tried this, I've noticed this technique on a few major website like ebay and Amazon (I think). Also a lot of banner ads do this, which makes me a bit mad since I think they are one of the many reason Flash died so fast, but that's for another post.

No comments:

Post a Comment