Interfacing SWF Files
Implementing an interface in a document class exposes an interface for an entire SWF file. This is really useful if you develop applications modularly, where modular SWFs can be swapped in and out of an application SWF without dependency problems.
Unfortunately if you use the embed metatag [Embed(source="module.swf")], your interface is hidden as if it doesn’t exist. I originally thought that it had been ‘stripped away’ somehow, though this is not quite the case. I want to be able to continue to use those interfaces for tests or for packaging builds that have locked-in modules, but using the Embed means of loading elements confuses matters considerably. There is a solution, though it is not straightforward!
SWFs Implementing Interfaces – Some Background
Not too long ago, my colleague Simone (aka Vizio) and were discussing making loaded SWFs satisfy an interface, then cast the loaded element to that interface when loaded, as follows. He wrote a blog post about the process:
http://blog.vizio360.co.uk/2009/10/using-swf-files-that-implement-a-specific-interface/
As Simone points out, this really ought to work, but you can never be sure with Adobe! There are many aspects of working with AS3 which are maddeningly unintuitive and feel like hacks. It is gratifying therefore, to find that this works just as you expect that it should.
In variation of Simone’s post, imagine the following interface:
public interface ExampleInterface
{
function get container():DisplayObjectContainer;
function update():void;
}
implemented by the following class:
public class ExampleSWF extends Sprite implements ExampleInterface
{
public function get container():DisplayObjectContainer { return loaderInfo.loader; }
public function update():void { ... do something interesting ... }
}
and loaded by the following main SWF:
public class Main extends Sprite
{
private var loader:Loader;
private var example:ExampleInterface;
public function Main()
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
loader.load(new URLRequest("ExampleSWF.swf"));
}
private function onLoaded(event:Event):void
{
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoaded);
example = ExampleInterface(loader.content);
trace(example);
}
}
The element is loaded and its cast to the ExampleInterface happens successfully.
SWFs Implementing Interfaces When Embedded
Replace the main class above with this:
public class Main extends Sprite
{
[Embed (source="ExampleSWF.swf")]
private var exampleClass:Class
private var example:ExampleInterface;
public function Main()
{
var object:* = new exampleClass();
example = ExampleInterface(object);
trace(example);
}
}
Intuitively this feels like it should work, but it doesn’t: example is null. If you use describeType to inspect the constructed object, the inheritance chain shows no evidence of ExampleSWF, nor of the ExampleInterface:
<extendsClass type="mx.core::MovieClipLoaderAsset"/> <extendsClass type="mx.core::MovieClipAsset"/> <extendsClass type="mx.core::FlexMovieClip"/> <extendsClass type="flash.display::MovieClip"/> <extendsClass type="flash.display::Sprite"/> <extendsClass type="flash.display::DisplayObjectContainer"/> <extendsClass type="flash.display::InteractiveObject"/> <extendsClass type="flash.display::DisplayObject"/> <extendsClass type="flash.events::EventDispatcher"/> <extendsClass type="Object"/> <implementsInterface type="mx.core::IFlexDisplayObject"/> <implementsInterface type="mx.core::IFlexAsset"/> <implementsInterface type="flash.events::IEventDispatcher"/> <implementsInterface type="flash.display::IBitmapDrawable"/> <implementsInterface type="mx.core::IBorder"/>
It does not work because the [Embed (source=...)] metatag wraps the embedded SWF into a Flex MovieClipLoaderAsset container. To extract the document class from the MovieClipLoaderAsset you need to do the following bit of classic ActionScript madness:
public class Main extends Sprite
{
[Embed (source="ExampleSWF.swf")]
private var exampleClass:Class
private var example:ExampleInterface;
public function Main()
{
var object:MovieClipLoaderAsset = new exampleClass();
var loader:Loader = Loader(object.getChildAt(0));
try
{
example = ExampleInterface(loader.contentLoaderInfo.content);
}
catch (error:Error)
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onInternalAssetLoaded);
}
}
private function onInternalAssetLoaded(event:Event):void
{
example = ExampleInterface(loader.contentLoaderInfo.content);
}
}
If you wish to use interfaces to increase the modularity of your code, then you can. If you want to then Embed those modules in certain contexts, you can as well. As with everything ActionScript however, don’t expect it to be simple!
-
http://twitter.com/alecmce alecmce
-
http://www.jacksondunstan.com/ Jackson Dunstan
-
http://www.jacksondunstan.com Jackson Dunstan
-
http://twitter.com/alecmce alecmce
-
http://twitter.com/TheFlashBum TheFlashBum
-
http://twitter.com/fixative fixative


