Skip to content
Nov 7 2009 / alecmce

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

    Extending @vizio360’s work, some more work on loading Interfaced SWFs: http://bit.ly/4mmdEn
    This comment was originally posted on Twitter

  • http://www.jacksondunstan.com/ Jackson Dunstan

    Great explanation. I can see this technique really coming in handy. Thanks for the write-up!

  • http://www.jacksondunstan.com Jackson Dunstan

    Great explanation. I can see this technique really coming in handy. Thanks for the write-up!

  • http://twitter.com/alecmce alecmce

    If you’re looking to interface DisplayObjects @TheFlashBum, may I plug my preferred solution: http://bit.ly/4mmdEn
    This comment was originally posted on Twitter

  • http://twitter.com/TheFlashBum TheFlashBum

    RT @alecmce: If you’re looking to interface DisplayObjects @TheFlashBum, may I plug my preferred solution: http://bit.ly/4mmdEn
    This comment was originally posted on Twitter

  • http://twitter.com/fixative fixative

    RT @alecmce: If you’re looking to interface DisplayObjects @TheFlashBum, may I plug my preferred solution: http://bit.ly/4mmdEn | Nice!
    This comment was originally posted on Twitter