Saturnboy
 4.27

Nested SWF communication

,

The ability to split up a large application in smaller, more manageable chunks is one of the pillars of enterprise development. The flash platform is littered with examples: AS3 is OO language, MXML components, SWCs, RSLs, SWFLoader, ExternalInterface, etc. In this post, I’m going to focus on using nested SWFs via SWFLoader. There a few other good posts out there, plus the Adobe docs, but I’ll try to tie everything together in one place.

My main interest in the nested SWFs approach is for performance reasons, both the reduced initial download size and the ability to defer component creation can make a big difference in the real world. And secondarily for testing reasons, because it’s just awesome to see a big enterprise app wired up via hudson with FlexMonkey and Fluint tests.

None of this would be very interesting without the ability to communicated between SWFs. Thankfully, there are a few good options when talking between Flash 9 SWFs, because they run together inside Flash Player in AVM2. Talking between Flash 9 or newer and Flash 8 or older is still possible, but because of the AVM1/AVM2 split you need to use LocalConnection which I’m not going to get into.

Child Application

How does SWF to SWF communication work? First, let’s start with a simple child app:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
 
    <mx:Script>
        <![CDATA[
            [Bindable] public var myProp:String = 'foo';
 
            private function btnClick():void {
                dispatchEvent(new Event('child'));
            }
            public function myFunc(color:uint):void {
                setStyle('backgroundColor', color);
            }
            public function myFuncReturn(i:int, j:int):int {
                return i + j;
            }
        ]]>
    </mx:Script>
 
    <mx:Label text="{'myProp = ' + myProp}" />
    <mx:Button label="dispatch event" click="btnClick()" />
</mx:Application>

The child app exposes one public property myProp, two public functions myFunc and myFuncReturn, and a custom event.

Main Application

The main app exercises all four options in the child SWF. But in an effort to mimic reality, I’m imposing the additional constraint that while the child’s API is known, the actual MXML is unavailable.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" verticalGap="12">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.events.FlexEvent;
            import mx.managers.SystemManager;
 
            private var childApp:Application;
 
            private function init():void {
                SystemManager(loader.content).addEventListener(
                        FlexEvent.APPLICATION_COMPLETE, childReady);
              }
 
              private function childReady(e:FlexEvent):void {
                  childApp = e.target.application as Application;
                  childApp.addEventListener('child', childEventHandler);
              }
              private function childEventHandler(e:Event):void {
                  Alert.show('got child event');
              }
 
              private function setChildProp():void {
                  if (childApp.hasOwnProperty('myProp')) {
                      childApp['myProp'] = 'bar';
                  }
            }
            private function callChildFunc():void {
                if (childApp.hasOwnProperty('myFunc') &&
                        childApp['myFunc'] is Function) {
                    childApp['myFunc'](0xFF00FF);
                }
            }
            private function callChildFuncReturn():void {
                if (childApp.hasOwnProperty('myFuncReturn') &&
                        childApp['myFuncReturn'] is Function) {
                    var val:int = childApp['myFuncReturn'](13, 4);
                    Alert.show('13 + 4 = ' + val);
                }
            }
        ]]>
    </mx:Script>
 
    <mx:Panel title="Child">
        <mx:SWFLoader id="loader" source="test_child.swf" complete="init()" />
    </mx:Panel>
 
    <mx:Button label="set child property" click="setChildProp()" />
    <mx:Button label="call child func" click="callChildFunc()" />
    <mx:Button label="call child func return" click="callChildFuncReturn()" />
</mx:Application>

In the main app, the child SWF is loaded via the SWFLoader MXML tag, which calls the init() function on completion. Then we wait for the child SWF to finish loading before casting it to an Application and wiring up a handler for our custom event. The other three child-to-parent communication options are exercised by the three button click callback functions. In each callback, we first check to see if the child property exists, and in the case of the two function calls, if it can be cast to a Function. In the Adobe docs, because the child MXML code is in the same folder as the parent MXML, they just use a simple cast prior to invoking functions:

FlexApp(loadedSM.application).setVarOne("Updated varOne!");

Which is great for them because they control both the parent and the child SWFs, but doesn’t really work when you have some 3rd party SWF you need to use or Creative lobs some crazy flash animation grenade into your cube at 5:15pm with a nice note to send over a working solution before you go home for the evening. Yep, that never happens.

There is another point that I often forget about MXML: instantiation via tags creates a public instance of the component. So for example, if we added id attributes to the child label and child button, then they would be directly accessible in the parent via childApp['childLabel'].text or childApp['childButton'].addEventListener(...). I find my sense of API cleanliness is deeply offend by this manner of communication, so I tend to stick with one of the four options I described above (probably why I always forget).

Here is the final product:

 »  Main App

Digging Deeper: If you really want to maximize client-side performance, you might find yourself loading and unloading child SWFs. In such a situation, it is important to be a good citizen and make sure your child SWFs clean up after themselves.

Files

Comments

Swathi

8.21.2009

1

Thanks for the blog post. It helped solve my real time problem. I was having problem getting the application property of the system Manager as null. Using the Flex Event solved it.

Perfunction

9.4.2009

2

Just as a tip that you might want to include. I found that if your child application has separate MXML components within itself, you have to catch and rethrow the events all the way up the chain to the parent.

Example:
Parent.swf
- final catch
Search.swf
–App.mxml: catch, final rethrow
–Criteria.mxml: catch, rethrow
–Results.mxml: initial throw

© 2017 saturnboy.com