Retaining getDefinition In An Embedded SWF
So more and more often lately I’ve been embedding asset swfs published through the Flash IDE as a way to interface with design teams and build component based library assets. This was going all fine when the library items where being used without much interference with my code, but inevitably that time happened. Which led to me hitting problem’s when compiling an assets.swf in to my projects and trying to access custom class functions when using getDefinition(). So the example below is how to successfully embed an assets.swf with custom class library items whilst retaining the ability to strictly type them ( and how I got to the solution )
Just for clarity here is the ExampleComponent.as file which I use as, you guessed it an example custom class.
package com.flashlounge.components {
import flash.display.Sprite;
public class ExampleComponent extends Sprite {
public function ExampleComponent() {
//trace("ExampleComponent.ExampleComponent()");
}
public function ExampleFunction(foo:String) : void {
//trace("ExampleComponent.ExampleFunction(" + foo + ")");
trace("Hello Main");
}
}
}
So it started off with what I have below, using the Embed tag to grab the published assets.swf and then specifying which symbol I would like to pull out from the library.
package com.flashlounge {
import flash.display.MovieClip;
import com.flashlounge.components.ExampleComponent;
public class Main extends MovieClip {
[Embed(source="../../../assets/assets.swf", symbol="ExampleComponent")]
private static var ExampleComponentClass : Class;
public function Main() {
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 0, true);
}
private function onAddedToStage(e : Event) : void {
//trace("Main.onAddedToStage(e)");
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
// Create a new Component class from the embedded class
var exampleComponent : ExampleComponent = new ExampleComponentClass();
addChild(exampleComponent);
// Call a custom function on the component
exampleComponent.ExampleFunction("Hello");
}
}
}
Great, except I kept getting “TypeError: Error #1034: Type Coercion failed: cannot convert com.flashlounge::Main_ExampleComponentClass@37289a61 to com.flashlounge.components.ExampleComponent” which after giving the error type a once over, looked like it was because I was conflicting the the static var class with the actual class. So I changed
// Create a new Component class from the embedded class
var exampleComponent : ExampleComponent = new ExampleComponentClass();
To
// Create a new Component class from the embedded class
var exampleComponent : ExampleComponent = new ExampleComponent();
Which then as you can imagine gave me errors about not finding the class, after doing some investigation I then proceeded to perhaps instantiate the embedded assets.swf and see if I could pull the component from the assets.swf when its within the context of my app
package com.flashlounge {
import flash.display.MovieClip;
import com.flashlounge.components.ExampleComponent;
public class Main extends MovieClip {
[Embed(source="../../../assets/assets.swf")]
private static var Assets : Class;
public function Main() {
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 0, true);
}
private function onAddedToStage(e : Event) : void {
//trace("Main.onAddedToStage(e)");
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
var assetsSwf: MovieClip = new Assets();
addChild(assetsSwf);
// Create a new Component class from the embedded class
var exampleComponent : ExampleComponent = assetsSwf.applicationDomain.getDefinition("ExampleComponent") as ExampleComponent;
addChild(exampleComponent);
// Call a custom function on the component
exampleComponent.ExampleFunction("Hello");
}
}
}
Yep you guess it this didn’t work either, so after some more investigation I stumbled on Keith Peter’s Embedding Resources with AS3 article which explained how to embed assets very well but looking through the comments it would seem people were hitting the same problems I was with custom class definitions.
So after much trail and tribulation I eventually found a solution which was clean and worked consistently. In essence the problem with embedding swf’s is the applicationDomain being different between swfs (eg main.swf’s applicationDomain being different from assets.swf’s applicationDomain). Below is my Main class which shows a swf being embedded then loaded through a Loader class as bytes which then sets the applicationDomain correctly allowing you to get your custom classes by definition, strictly typed with the ability to run functions on them.
package com.flashlounge {
import com.flashlounge.components.ExampleComponent;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.MovieClip;
import flash.events.Event;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.utils.ByteArray;
public class Main extends MovieClip {
private var _assetLoader : Loader;
private var _asset : LoaderInfo;
[Embed(source="../../../assets/assets.swf", mimeType="application/octet-stream")]
public var Assets : Class;
public function Main() {
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e : Event) : void {
//trace("Main.onAddedToStage(e)");
// We take the embedded assets swf as a ByteArray and then load it within the Main's ApplicationDomain
// Thereby allowing Main to access its definitions.
_assetLoader = new Loader();
_assetLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onAssetLoaderComplete);
_assetLoader.loadBytes(new Assets as ByteArray, new LoaderContext(false, ApplicationDomain.currentDomain));
}
public function onAssetLoaderComplete(e : Event) : void {
//trace("Main.onAssetLoaderComplete(e)");
// Recieve the embedded swf's LoaderInfo
_asset = e.currentTarget as LoaderInfo;
// Create an ExampleComponent from the embedded assets.swf
var exampleComponent : Class = getClass("ExampleComponent");
var anExampleComponent : ExampleComponent = new exampleComponent();
addChild(anExampleComponent);
// Call a custom function on the component
anExampleComponent.ExampleFunction("Hello");
}
// Helper function for getting classes without errors
private function getClass(className : String) : Class {
//trace("AssetsProxy.getClass(" + className + ")");
try {
return _asset.applicationDomain.getDefinition(className) as Class;
}
catch (e : Error) {
trace("getClass(" + className + ") Failed");
}
return null;
}
}
}