Decouple Externally Loaded SWFs in AS3
Published by Nicholas Dunbar on February 2nd, 2012
How to decouple externally loaded movieClips from their loader in Actionscript 3 (AS3)
Here is a common problem:
TypeError: Error #1034: Type Coercion failed: cannot convert com.yourUrl.mainClass.SomeClass@48cb821 to com.yourUrl.mainClass.SomeClass at com.yourUrl.mainClass::MainClass/onLoadSwfHandler()[C:\Documents and Settings\flash_guru\Desktop\source\trunk\src\ActionScript_Guru\classes\com\yourUrl\mainClass\MainClass.as:489]
There are two reasons you could get this error that come to mind.
1.) Durring the publishing of this externally loaded SWF a compile time error occured that you did not catch. In this case the error would more likely be
TypeError: Error #1034: Type Coercion failed: cannot convert flash.display::MovieClip@48cb821 to com.yourUrl.SomeClass
2.) There are classes compiled into the SWF that are also compiled into MainClass that have the same name but who’s versions are not the same. For instance say you have a LoaderClass.as that you import into SomeClass.as and MainClass.as. You then publish SomeClass.fla, then change LoaderClass.as then publish MainClass.fla but forget to republish the other class.
If you want to take advantage of compile time checking then in MainClass.as you would have the object where the SWF will be stored typed as SomeClass. That would be why there was a coercion error. Because the SomeClass that is the Document Class of SomeClass.fla is not the same as the Document Class of SomeClass in MainClass.
What is the big deal? Why don’t you just republish both all the time? This is fine if you are working on small scale projects. But if we have 100’s of files that use LoaderClass.as and we are running a site that get’s 1,000 hits plus a day and we have to update the loader class this would slow us down a bunch. It is important to remove compiler interdependency on such classes. In this case you would only want to have to republish or recompile MainClass with the new LoaderClass.as and then the MainClass would pass out the loader class to the externally loaded SWFs who have no knowledge of the loader class except for its public API. This works best for testing and works best for updating the website with minimal disruption.
To do this while maintaining compile time checking of the loaded objecst API, you need to use interfaces. These are regarded mostly as being used to make sure that object APIs conform to a certain standard. But they are so much more useful than that. A real ActionScript Guru knows that an interface acts as an outlet or socket and a class’ API Acts as a Plug that fits into that outlet or socket. Anything that has the right shaped plug will run in the socket. This is also an essential concept to master if you want to do polymorphism in flash. Think of the metaphor of plugging different appliances into the same outlet.
So how do we decouple the two? There are two things that need to happen. First you have to make sure that MainClass has a socket for SomeClass to plug into. This eliminates MainClass’s knowledge of SomeClass’s interior workings, thus allowing us to update any of the functions that MainClass uses on SomeClass with only publishing someClass.swf for mainClass to use the updated functionality. Secondly we have to make sure SomeClass.as has no knowledge of LoaderClass’s interior workings.
Interfaces act as a meeting point between the two classes. If the API changes for either class then those classes will have to be republished, but if the code inside of each of those functions changes you only need to update the SWF that has ownership of that class. Also with interfaces if you change the API of the class, the interface will not let you use that new function without adding it to the interface. You would be made aware of this during compile time which is the whole point of compile time checking, to keep you from making mistakes.
First step:
Create ISomeClass and type the container for the externally loaded SWF SomeClass as ISomeClass. When the load is complete you should be able to put the loaded SWF in an Object and then type that reference to ISomeClass.
Second step:
We can create one LoaderClass that is handed out to its externally loaded SWFs by making the LoaderClass implement something called ILoaderClass.as.
Here is an example:
LoaderClass.as :
public class LoaderClass extends IEventDispatcher implements ILoaderClass
{
}
ILoaderClass.as :
package com.yourUrl.product {
import flash.events.IEventDispatcher;
import flash.display.DisplayObjectContainer;
public interface ILoaderClass extends IEventDispatcher
{
function loadAsset(varURL_str:String,loc_mc:DisplayObjectContainer=null):void;
}
}
We can then pass a reference to that class into an interface that exists inside of SomeClass.as similarly to how we did it in MainClass.as
Check out the next article for more information (How to organize packages for decoupling externally loaded SWFs.)