Dynamically loading a SWF and instantiating a Class in that SWF

There are a few blog posts out there on how to do this, but most of them involve a solution that requires a dummy reference in your Application code to the class you wish to instantiate, which forces the compiler to pull in the class, and therefore, allows getDefinition() to work.  But that solution defeats the whole purpose of loading a SWF dynamically.

If you want to load a SWF remotely, and then instantiate a class within that SWF at run-time, you are in the right place.

In the example below, the “application” is Foo and the class we’ll instantiate will be called Bar.  Let’s look at the SWF containing Bar first. Our goal, is to load the SWF that contains Bar and instantiate Bar in our application.

Dynamic.swf

Below is the root level class of the SWF we’ll dynamically load, let’s say it’s called Dynamic.swf. Notice the private var of type Bar. This is necessary to get the compiler to pull the Bar class into our Dynamic.swf. The idea here, is we’ll build Dynamic.swf and put it up on a server somewhere, or localhost so we can test this.

package
{
	import com.charlespatricknewman.sample.Bar;

	import flash.display.Sprite;

	public class Dynamic extends Sprite
	{
		// Need a dummy reference
		private var bar:Bar;

		public function Dynamic()
		{

		}
	}
}

And here is the Bar class we want to instantiate in our Application at run-time:

package com.charlespatricknewman.sample
{
	public class Bar
	{
		public function Bar()
		{
			trace(">>> Bar() constructor!");
		}

		public function doSomething():void
		{
			trace(">>> Bar.doSomething() called!");
		}
	}
}

All this class does is trace out some text, but this is sufficient to prove this is working.

The Application

Next is Foo, the application that will load the SWF and instantiate Bar.  Note this project has no reference or dummy variables of type Bar.

Here is the root level class:

package
{
import com.charlespatricknewman.sample.DynamicClassLoader;

import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

public class Foo extends Sprite
{
	public function Foo()
	{
		this.addEventListener(Event.ADDED_TO_STAGE,
					onAddedToStage);
	}

	private function onAddedToStage(event:Event):void
	{
		go();
	}

	private function go():void
	{
		var loader:DynamicClassLoader = new DynamicClassLoader();
		setupListeners();
		loader.loadSWF("http://localhost/test/Dynamic.swf",
				"com.charlespatricknewman.sample.Bar");

		function setupListeners(add:Boolean=true):void
		{
			if (add)
			{
				loader.addEventListener(Event.COMPLETE,
					onClassLoaded);
			}
			else
			{
				loader.removeEventListener(Event.COMPLETE,
					onClassLoaded);
			}
		}

		function onClassLoaded(event:Event):void
		{
			setupListeners(false);
			var Bar:Class = loader.instantiateClass(
				"com.charlespatricknewman.sample.Bar");
			var bar:Object = new Bar();
			bar.doSomething();
		}
	}
}
}

And here is the DynamicClassLoader class you see referenced above. This class could reside in the Application SWF or a library (SWC) the application uses at compile time. It’s job is to load the SWF and allow the Application to instantiate a class in the loaded SWF.

package com.charlespatricknewman.sample
{
 import flash.display.Loader;
 import flash.errors.IllegalOperationError;
 import flash.events.Event;
 import flash.net.URLRequest;
 import flash.system.ApplicationDomain;
 import flash.system.LoaderContext;

 public class DynamicClassLoader extends Loader
 {
	public function DynamicClassLoader()
	{
		super();
		contentLoaderInfo.addEventListener(Event.COMPLETE,
						onComplete);
	}

	public function loadSWF(swfPath:String, classPath:String):void
	{
		if (ApplicationDomain.currentDomain.hasDefinition(classPath))
		{
			throw new IllegalOperationError("Class is already loaded!");
		}

		var request:URLRequest = new URLRequest(swfPath);
		var context:LoaderContext = new LoaderContext();

		context.applicationDomain = ApplicationDomain.currentDomain;

		load(request, context);
	}

	public function instantiateClass(classPath:String):Class
	{
		return ApplicationDomain.currentDomain.getDefinition(classPath)
				as Class;
	}

	private function onComplete(event:Event):void
	{
		dispatchEvent(new Event(Event.COMPLETE));
	}
}
}

Due to the security restrictions in the Flash Player, both Dynamic.swf and the Application SWF need to be loaded from either localhost or a web server. You might also need a crossdomain.xml file on the web server, but it’s best to try this from localhost first so you can see it working.

This might be a good solution if you have a SWF or a SWC that contains a large amount of code that is rarely used. Adding that code to your SWF or SWC might cause an unnecessarily large SWF. But loading that bloated Class only when you need it, will provide a much faster initial download and startup for your end users.