Categories Displayed in Flash

One swf to rule them all: Coding a single swf for both web and AIR runtimes.

As cool as AIR is, by default as soon as you link against flash.filesystem.*, you lose portability onto web.

Consider an app that takes a saves anything. Say a webcam photo manipulator. On the desktop it may be great to save the JPG locally, when running on the web it would post to the webserver. Ideally it's the same swf. Unfortunately if you run the swf with links to flash.filesystem.File you get an error:

VerifyError: Error #1014: Class flash.filesystem::File could not be found.
at global$init()

As mentioned on Big SpaceShip Labs this is a runtime errors not compile time errors, and since they are so low level you can't try/catch them. Error boxes is the last thing your viewers want to see as the first thing they hit you app!

  1. import flash.filesystem.*;
  2. import flash.system.Capabilities;
  3. import com.troyworks.io.*;
  4.  
  5. var file:File = File.userDirectory;
  6. var fileP:IFile = new FileProxy();
  7. trace(fileP.userDirectory.name);
  8.  
  9. trace("playerType: " + Capabilities.playerType);

However there are ways around this, but they do involve a few more steps.

  1. Creating and interface to the various File, and FileSystem, and programming to that interface
  2. Creating File/etc. Wrapper objects that use that same interface the AIR exposes.
  3. Putting all the (2) + AIR packages in a separate swf that is loaded at runtime,
  4. Detect if it's running in the AIR, and loading and using that library.

This same approach can be used to make the swf's persistance features work with other Projectors, e.g. BuddyAPI, Zinc, WebDAV, etc. Provided the Interface can be abstracted enough.
1. Creating the flash.filesystem.File Interface
This is a reasonable amount of work as the flash.filesystem.File doesn't conform to an public interface, as far as I can tell, and it's complicated as most of the calls return "File" so they to have be rewrapped. So something simple is:

  1. package com.troyworks.io {
  2.  
  3. public interface IFile {
  4.  
  5. function get userDirectory() : IFile;
  6. function get name():String;
  7. }
  8.  
  9. }

2. Creating the Library

You'll need two swfs. The library ("AirLib.swf") and the caller (OfflineOnline.swf).

AirLib.swf frame 1 has the following script

  1. import flash.filesystem.*;
  2. import flash.system.Capabilities;
  3. import com.troyworks.io.*;
  4. import flash.text.TextField;
  5.  
  6. var ClassReference:Class = getDefinitionByName("com.troyworks.io.FileProxy") as Class;
  7. var fileP:IFile = new ClassReference();
  8.  
  9. //var file:File = File.userDirectory;
  10. var _fileP:IFile = new FileProxy();
  11. trace("useDIrectoryName " + fileP.userDirectory.name);
  12. status_txt.appendText("useDIrectoryName " +fileP.userDirectory.name);
  13.  
  14. trace("playerType: " + Capabilities.playerType);

Clicking Debug should show the output in the Debug Output window and the textfield.

3. Detecting the AIR Player Runtime
this is pretty easy, just using the system.Capabilities:

  1. import flash.system.Capabilities;
  2.  
  3. trace("playerType: " + Capabilities.playerType);  //AIR = playerType: Desktop , FlashIDE = "External" Project = "Standalone"

4. Creating Library User

Then providing the loaded swf access to the callling swf via the LoaderContext and then later retrieving the class via getDefinitionByName

  1.  
  2. /////////////////////////////////////
  3. _loader = new Loader();
  4. var ldrContext:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
  5.  
  6. configureListeners(_loader.contentLoaderInfo);
  7. _loader.addEventListener(Event.COMPLETE, completeHandler);
  8. var request:URLRequest = new URLRequest(url);
  9. _loader.load(request, ldrContext);
  10. <blockquote>function completeHandler(event:Event):void {
  11. trace("completeHandler: " + event);
  12.  
  13. var clip:DisplayObject = DisplayObject(ExternalLibLoader(event.target).loadedClip);
  14.  
  15. addChild(clip);
  16. var ClassReference:Class = getDefinitionByName("com.troyworks.io.FileProxy") as Class;
  17. var fileP:IFile = new ClassReference();
  18. status_txt.appendText(fileP.userDirectory.name);
  19.  
  20. }

Conculsion

At this point when run the Library user will selectively load the AIR library or not, depending on what environment it's loaded in. The output swf can be run in AIR 1.0, Flash Projectors, and the web player without giving errors and doing sensible persistence based on what options are available.

No code is available yet, I still have a lot of work to do to wrap the File and related classes.

Add a Comment:

You must be logged in to post a comment.