3 Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.4 View Mediator and ViewNavigator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.5 Bean Life Cycle Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.6 Event Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.7 Service Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.8 Client Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.9 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Advanced Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 AutowiredTestCase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.2 Chaining API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.3 Custom Metadata Processors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.4 Module Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.5 Support for the Command pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Best Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 Presentation Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.6 Release Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7 IDE Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.8 External Community Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.9 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.9.1 Updating a Swiz 0.6.4 application to Swiz 1.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.10 How To Contribute to Swiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.11 Presentations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.12 FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 2 3 9 10 11 13 15 16 20 22 23 24 25 25 27 33 33 34 35 35 37 37 41 43 43 43 44 45 45
_sidebar
Note About Comments Feel free to give feedback on the documentation with a comment. But for technical questions, please use the Swiz mailing list.
Save as HTML or PDF You can export this entire Wiki in HTML or PDF format.
Home
What is Swiz?
Swiz is a framework for Adobe Flex, AIR, and Flash that aims to bring complete simplicity to RIA development. Swiz provides: Inversion of Control / Dependency Injection Event handling A simple life cycle for asynchronous remote method invocations A framework that is decoupled from your application code Compatibility with both Flex and non-Flex AS3 projects In contrast to other major frameworks for Flex, Swiz: Imposes no JEE patterns on your code No repetitive folder layouts No boilerplate code on your development Does not require you to extend framework-specific classes Swiz represents best practices learned from the top RIA developers at some of the best consulting firms in the industry, enabling Swiz to be simple, lightweight, and extremely productive.
Visualizing Swiz
Sometimes pictures can help solidify an idea better than words can. With that in mind, here are basic representations of two core Swiz features: dependency injection and event handling. Dependency Injection Event Handling
Quick Start
Page Contents:
A Lightning Look at Configuration The "Big Three": Dependency Injection, Event Handling, and Server Interaction Adding Dependency Injection Dispatching and Handling Events Talking to the Server There's More Where That Came From
You can download this Quick Start as a Flash Builder FXP or Zip file. It's also available in the Swiz Examples repository on GitHub, along with more examples.
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:view="org.swizframework.quickswiz.view.*" xmlns:config="org.swizframework.quickswiz.config.*" xmlns:swiz="http://swiz.swizframework.org"> <fx:Declarations> <swiz:Swiz> <!-- BeanProviders simply contain the non-display objects that Swiz should process. --> <swiz:beanProviders> <config:Beans /> </swiz:beanProviders> <swiz:loggingTargets> <swiz:SwizTraceTarget id="myTraceTarget" /> </swiz:loggingTargets> <swiz:config> <!-- The eventPackages value tells Swiz the path to your Event classes, and viewPackages is an optional value that speeds up the processing of display classes. --> <swiz:SwizConfig eventPackages="org.swizframework.quickswiz.event.*" viewPackages="org.swizframework.quickswiz.view.*" /> </swiz:config> </swiz:Swiz> </fx:Declarations> <s:layout> <s:VerticalLayout horizontalAlign="center" paddingTop="20" /> </s:layout> <view:UserForm id="userForm" /> </s:Application>
Non-visual components that you want Swiz to manage are defined in a BeanProvider tag. Any beans that you define within the BeanProvider are processed by Swiz for dependency injection and the creation of event handlers. In the following example, a UserService and a UserController are created:
<swiz:BeanProvider xmlns:swiz="http://swiz.swizframework.org" xmlns:service="org.swizframework.quickswiz.service.*" xmlns:controller="org.swizframework.quickswiz.controller.*"> <service:UserService id="userService"/> <controller:UserController id="userController"/> <!-- We'll use the Swiz ServiceHelper to help simulate a server-side call. --> <swiz:ServiceHelper id="serviceHelper" /> </swiz:BeanProvider>
The "Big Three": Dependency Injection, Event Handling, and Server Interaction
The three most commonly used features of Swiz are its dependency injection capabilities, its event handling features, and its server interaction utilities. Let's look at how each of these work.
package org.swizframework.quickswiz.controller { import org.swizframework.quickswiz.model.User; import org.swizframework.quickswiz.service.UserService; public class UserController { [Inject] public var userService : UserService; [Bindable] public var currentUser : User; } }
In addition to injecting a bean, you can inject individual bean properties. In this example, the currentUser property of the UserController is injected into a UserForm visual component. Note that it is not necessary for the UserForm to be declared as a bean in the BeanProviders tag. When visual components are added to the display list, Swiz automatically inspects them and processes any metadata tags that are found.
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <fx:Script> <![CDATA[ import org.swizframework.quickswiz.event.UserEvent; import org.swizframework.quickswiz.model.User; [Bindable] [Inject( source="userController.currentUser", bind="true" )] /** * We could inject the whole controller instance, but we only need * one property from the controller, the current user, so we just * inject that property. */ public var user : User; ]]> </fx:Script> <mx:Form> <mx:Text id="userId" text="{isNaN( user.id ) ? 'N/A' : user.id}" /> <s:TextInput id="firstName" text="{user.firstName}" /> <s:TextInput id="lastName" text="{user.lastName}" /> <s:TextInput id="email" text="{user.email}" /> <s:Button label="Save" /> </mx:Form> </s:Panel>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <fx:Script> <![CDATA[ import org.swizframework.quickswiz.event.UserEvent; import org.swizframework.quickswiz.model.User; [Bindable] [Inject( source="userController.currentUser", bind="true" )] /** * We could inject the whole controller instance, but we only need * one property from the controller, the current user, so we just * inject that property. */ public var user : User; /** * Handle the user hitting the save button. We capture the form data * and dispatch a standard Flex event. No Swiz-specific events or * special central dispatcher needed! */ private function saveUser() : void { user.firstName = firstName.text; user.lastName = lastName.text; user.email = email.text; var event : UserEvent = new UserEvent( UserEvent.SAVE_USER_REQUESTED ); event.user = user; dispatchEvent( event ); } ]]> </fx:Script> <mx:Form> <mx:Text id="userId" text="{isNaN( user.id ) ? 'N/A' : user.id}" /> <s:TextInput id="firstName" text="{user.firstName}" /> <s:TextInput id="lastName" text="{user.lastName}" /> <s:TextInput id="email" text="{user.email}" /> <s:Button label="Save" click="saveUser()" /> </mx:Form> </s:Panel>
A look at the UserEvent confirms that this is just a simple, standard event. The only thing to note here is that the event has bubbles set to true. This allows the event to bubble up the display list so that it can be handled by Swiz:
package org.swizframework.quickswiz.event { import org.swizframework.quickswiz.model.User; import flash.events.Event; public class UserEvent extends Event { public static const SAVE_USER_REQUESTED : String = "saveUser"; public var user : User; /** * This is just a normal Flex event. The only thing to note is that we set 'bubbles' to true, * so that the event will bubble up the display list, allowing Swiz to listen for your events. */ public function UserEvent( type:String ) { super( type, true ); } } }
We've seen how to dispatch events from the display list, but how do we handle the events? Swiz provides a [EventHandler] metadata tag to do this. In the example below, we have expanded the UserController to add an event handler method. In this case, when Swiz encounters an event of type UserEvent.SAVE_USER_REQUESTED, it will automatically invoke the saveUser method in the UserController. By specifying a value of "user" for the properties attribute, Swiz will locate the user property in the UserEvent and pass this as a parameter to the handler method:
package org.swizframework.quickswiz.controller { import org.swizframework.quickswiz.model.User; import org.swizframework.quickswiz.service.UserService; public class UserController { [Inject] public var userService : UserService; [Bindable] public var currentUser : User; [PostConstruct] /** * [PostConstruct] methods are invoked after all dependencies are injected. * In this example, we set up a default user after the bean is created. */ public function createDefaultUser() : void { currentUser = new User(); } [EventHandler( event="UserEvent.SAVE_USER_REQUESTED", properties="user" )] /** * Perform a server request to save the user */ public function saveUser( user : User ) : void { // TODO: Add server interaction } } }
One last feature to note regarding event dispatching. We've seen how an event dispatched from the display list will bubble up until it is processed
by Swiz. But what about dispatching events from non-visual components like controllers and services? Swiz offers the [Dispatcher] metadata tag to meet this need. In the UserService shown below, Swiz will automatically inject an event dispatcher into the dispatcher property. Any events dispatched though this dispatcher will also be processed by Swiz and trigger any event handler methods associated with the event:
package { import import import org.swizframework.quickswiz.service org.swizframework.quickswiz.model.User; flash.events.IEventDispatcher; mx.rpc.AsyncToken;
import org.swizframework.utils.services.MockDelegateHelper; public class UserService { [Dispatcher] /** * The [Dispatcher] metadata tag instructs Swiz to inject an event dispatcher. * Events dispatched via this dispatcher can trigger methods annotated with [EventHandler]. */ public var dispatcher : IEventDispatcher; /** * To avoid a live server dependency, we use a Swiz * helper class to let us create fake AsyncTokens */ private var mockHelper : MockDelegateHelper; public function UserService() { mockHelper = new MockDelegateHelper(); } /** * In a real app, we'd invoke a RemoteObject, HTTPService, etc. * For this simple example, we'll set a random ID on the User * to simulate the process of saving a User. */ public function saveUser( user : User ) : AsyncToken { user.id = Math.round( Math.random() * 100 ); return mockHelper.createMockResult( user ); } } }
public class UserController { [Inject] public var userService : UserService; [Inject] public var serviceHelper : ServiceHelper; [Bindable] public var currentUser : User; [PostConstruct] /** * [PostConstruct] methods are invoked after all dependencies are injected. * In this example, we set up a default user after the bean is created. */ public function createDefaultUser() : void { currentUser = new User(); } [EventHandler( event="UserEvent.SAVE_USER_REQUESTED", properties="user" )] /** * Perform a server request to save the user */ public function saveUser( user : User ) : void { serviceHelper.executeServiceCall( userService.saveUser( user ), handleSaveUserResult ); } /** * Handle the server call result */ private function handleSaveUserResult( event : ResultEvent ) : void { // Show an Alert just to make it obvious that the save was successful. Alert.show( 'User saved successfully!' ); } } }
User Guide
Welcome to the Swiz User Guide!
Please select a subsection from the menu to view more details about how Swiz works.
Configuration
Page Contents:
Configuration Overview Logging setUpEventType, setUpEventPhase and setUpEventPriority tearDownEventType, tearDownEventPhase and tearDownEventPriority Default Dispatcher
Configuration Overview
You configure each Swiz instance using the SwizConfig class. It allows you to modify common settings, and to provide values that allow your tags and code elsewhere to be more concise. Below is an example with all properties shown. Where applicable, they have been set to their default values. However, in most cases, the default values should work fine (see the note below on Configuration Defaults).
<swiz:Swiz> <swiz:beanProviders> <local:MyBeans /> </swiz:beanProviders> <swiz:loggingTargets> <swiz:SwizTraceTarget id="myTraceTarget" /> </swiz:loggingTargets> <swiz:config> <swiz:SwizConfig setUpEventType="{ Event.ADDED_TO_STAGE }" setUpEventPhase="{ EventPhase.CAPTURING_PHASE }" setUpEventPriority="50" tearDownEventType="{ Event.REMOVED_FROM_STAGE }" tearDownEventPhase="{ EventPhase.CAPTURING_PHASE }" tearDownEventPriority="50" eventPackages="com.foo.event.*, org.bar.event.*" viewPackages="com.foo.view.*, org.bar.view.*" defaultFaultHandler="handleUnhandledFaults" defaultDispatcher="global" /> </swiz:config> </swiz:Swiz>
Configuration Defaults Unless you are specifying your own set up and tear down values, the only configuration values that commonly need to be set are eventPackages and viewPackages. If you are using Swiz's support for server communication, you may also set defaultFaultHandler.
Specifying Packages Note that due to limitations in the AS3 reflection API, when you define eventPackages, you must specify each package individually. Children of your specified packages cannot be resolved and must be explicitly set. This limitation does not apply to viewPackages because they are handled differently, but for consistency it may be useful to use the same rules to define both sets of packages.
Logging
As you can see above, Swiz includes a basic logging target called SwizTraceTarget to trace debugging information to the console. Due to the way the MXMLC compiler works, it was not possible to use the built-in Flex logging target(s), because it increases the size of the Swiz swc by an unacceptable amount. If necessary, you can extend the AbstractSwizLoggingTarget to customize the output.
Default Dispatcher
The default dispatcher value is "global". This means that in the case where a Swiz instance is the child of another Swiz instance, the child will use the parent dispatcher. This allows for easy communication between Swiz instances, since they all share the same event dispatcher. However, if you want to force a child Swiz to use it's own dispatcher, you can set this value to "local". In most cases, developers should not need to change the default value ("global"). More information on parent-child Swiz instances can be found in the section on Module support.
IoC Container
Page Contents:
The BeanProvider Class Prototype Beans Externalizing Configuration Values
Most applications define at least one Beans.mxml in the root package and pass it to the Swiz tag's beanProviders property in the applications main MXML file. For clarity and simplicity, it is recommended that you define your beans in a separate file, using BeanProvider as the root tag, as shown above. However, if for some reason you choose to define your beans inline in the Swiz tag, you'll need to wrap your objects with the Bean tag to ensure they are processed correctly:
<swiz:Swiz> <swiz:beanProviders> <swiz:BeanProvider> <swiz:Bean name="applicationModel"> <model:ApplicationModel /> </swiz:Bean> <swiz:Bean name="userController"> <control:UserController /> </swiz:Bean> </swiz:BeanProvider> </swiz:beanProviders> <swiz:config> <swiz:SwizConfig eventPackages="com.foo.events, org.bar.events" viewPackages="com.foo.views, org.bar.events" /> </swiz:config> </swiz:Swiz>
Prototype Beans
Similar to Springs prototype scope, Swiz provides a Prototype tag. Prototype enables a few different things: Constructor injection you can pass up to 8 arguments to your beans constructor Unique copy for each injection target (like prototype scope behavior in Spring) Deferred instantiation Prototype beans will not be created until they are needed for injection
<swiz:Prototype id="editViewPresoModel" type="{ EditViewPresentationModel }" constructorArguments="{ someOtherBean }" />
If you want deferred instantiation and/or constructor injection but not a unique copy for each injection you can set Prototypes singleton property to true.
A Prototype bean created and injected into a view is not destroyed when the view is removed. Swiz cannot know what else you may have done with the Prototype. If you wish to tear down a Prototype bean when a view is removed, implement a [PreDestroy] method and dispatch a TEAR_DOWN_BEAN event for the Prototype bean.
Finally, you can create an event handler to execute after the external data is loaded (remember to add "org.swizframework.externalconfig.event" to your eventPackages):
[EventHandler( event = "ConfigLoaderEvent.CONFIG_LOAD_COMPLETE", properties = "configData" )] public function onConfigLoadComplete( configData : Object ) : void { // You can now reference the value with syntax such as: // configData.config.value1, configData.config.value2, etc. }
Dependency Injection
Page Contents:
Dependency Injection Overview Inject by type Inject by name Inject bean property Two way bindings Setter injection Injection destination
Due to restrictions imposed by the AVM, Swiz cannot inject into private, protected or internal members.
By default, injections in view components happen during the ADDED_TO_STAGE event. This behavior can be modified by changing the Swiz configuration. It is important to understand that this means that injections occur after the CREATION_COMPLETE event. If you need to trigger logic during creation that depends on injections, use a [PostConstruct] method.
Inject by type
If you define exactly one bean of a given type you can inject it into a matching destination with a plain [Inject] tag. Note this also supports injecting a concrete implementation of an interface to which the destination is typed. In other words, if a property decorated with [Inject] is of type IUserService, Swiz will correctly inject a bean implementing that interface, such as UserService or MockUserService.
Best Practice You should inject by type whenever possible. It avoids reliance on string based names, which can lead to run time errors caused by misspellings.
Automatic Creation and Injection of Built-In Helper Classes If Swiz encounters an [Inject] tag for a property typed to ServiceHelper, IServiceHelper, URLRequestHelper, IURLRequestHelper, or MockDelegateHelper, and there is no matching Bean defined in a BeanProvider, Swiz will automatically create a bean of the correct type and inject it for you.
Inject by name
If you have more than one bean of a given type defined you can request that a bean with a specific name be injected. This bean name must match the id attribute of the bean's tag as defined in your BeanProvider.
[Inject( source="userService" )] public var service:RemoteObject; ... // will inject this from BeanProvider <mx:RemoteObject id="userService" />
Quick Tip "source" is the default attribute of [Inject], so the above tag could also be written as [Inject( "userService" )]
Quick Tip If the bean property is bindable and the decorated property is public, you can have Swiz create a binding to the source property by adding bind="true" to the [Inject] tag. Note that due to bugs in the underlying Flex SDK, it is impossible to confirm that a nested property is bindable. As a result, Swiz can only bind to properties of the target object.
Setter injection
Swiz can also inject values into single argument methods. All of the attributes and policies mentioned above apply to setter injection as well, with
Injection destination
Sometimes it's useful to define an injection someplace other than directly on a property/method. This is especially true in MXML views, where decorating a child component's property is not possible. Rather than creating a local variable and binding your component property to it, you can simply use the destination attribute of [Inject]. If this were defined in an MXML file, it would be wrapped in a Metadata tag.
[Inject( source="userModel.currentMode", destination="modeViewStack.selectedIndex" )]
Take care to ensure that the injection destination will be created and available by the time your injections are processed. The injection processing trigger can be configured in SwizConfig, but defaults to the ADDED_TO_STAGE event.
[ViewAdded] public function userListViewAdded( view : UserListView ) : void { this.view = view; } [ViewRemoved] public function userListViewRemoved( view : UserListView ) : void { this.view = null; }
The existence of these tags in a Prototype bean will not trigger the creation of that bean just because a matching view is added/removed. The bean instance containing these tags must already exist when the view event occurs.
Note that a ViewNavigator injection is not guaranteed to be populated by the time [PostConstruct] methods run, due to the timing around the creation of the ViewNavigator by the Flex mobile SDK.
[Dispatcher] public var dispatcher : IEventDispatcher; private function createNewBean() : void { userModel : UserModel = new UserModel(); // Swiz will create a bean for the userModel, and process any metadata in it. dispatcher.dispatchEvent( new BeanEvent( BeanEvent.SET_UP_BEAN, userModel ) ); }
Both SET_UP_BEAN and ADD_BEAN will process the target object as a bean. The difference between these events is that ADD_BEAN will also add the bean as a singleton to the BeanFactory cache, while SET_UP_BEAN does not add the new bean to the cache.
private function destroyBean() : void { // Swiz will destroy the userModel bean, clean up any injected objects, and delete any injection bindings. dispatcher.dispatchEvent( new BeanEvent( BeanEvent.TEAR_DOWN_BEAN, userModel ) ); }
The tear down events mirror the set up events discussed previously: TEAR_DOWN_BEAN cleans up the target by removing injections, event handlers, etc., while REMOVE_BEAN cleans up the bean as well as removing it from the singleton cache in the BeanFactory.
Since BeanEvent is a bubbling event, you can dispatch it from a view. If you dispatch them from non-view beans, be sure you use an injected dispatcher so that Swiz can handle the event.
If necessary, you can directly call the setUpBean() and tearDownBean() methods on the BeanFactory. Since these methods both take a Bean instance as an argument, you can use the createBeanForSource() method on the BeanFactory to generate a Bean instance that you can then pass into the set up and tear down methods. However, in general the event-based approach to creating and tearing down beans should be the preferred approach.
Similarly, a public method decorated with [PreDestroy] will be called when a bean is destroyed by Swiz. This would happen if a UI component is removed from the stage, or a module is unloaded.
package org.swizframework.quickswiz.controller { import org.swizframework.quickswiz.service.UserService; public class UserController { [Inject] public var userService : UserService; [PreDestroy] /** * [PreDestroy] methods are invoked when a bean is destroyed. */ public function clearPollingTimer() : void { userService.stopPolling(); } } }
SwizConfig Options
Six configuration options are available in the SwizConfig object to specify how UI components are handled by the framework. These are setUpEventType, setUpEventPhase, setUpEventPriority, and the corresponding tearDownEventType, tearDownEventPhase, and tearDownEventPriority. Normally, you can leave these at their default values. But if you need to, you can modify these to alter how Swiz creates and destroys beans that are UI components. The default setUpEventType is "addedToStage". This means that whenever a UI component is added to the stage, Swiz will inspect the component and process any metadata it finds. Any dependency injections and event mediators will happen at this time. As mentioned, you may change this value if "addedToStage" is not ideal for your situation. "creationComplete" is another commonly used setUpEventType.
Be careful if you use an event type that occurs early in the Flex component life cycle, such as "preinitialize", since child components have not been created yet.
At the other end of the bean life cycle, the default tearDownEventType is "removedFromStage". This means that when a UI component is removed from the stage, Swiz will perform clean up activities such as removing event mediators. If you require even more fine-grained control, you can specify alternative values for the phase and priority used for the set up and tear down of beans. Typically, these won't need to be changed, but the options are there in case they are needed.
You can also use the ISetUpValidator and ITearDownValidator interfaces with UI components to control whether set up or tear down are allowed.
The following table shows the steps that Flex and Swiz will go through on application startup: Type Flex Swiz Swiz Swiz Swiz Swiz Swiz Swiz Swiz Swiz Swiz Swiz Swiz Swiz Swiz Flex Flex Step Preinitialize event dispatcher set Swiz created event domain set Global dispatcher set Processors initialized Bean factory initialized setUpEvent and tearDownEvent values set from SwizConfig Beans defined in the BeanProvider(s) are processed (per bean) beanFactory.setUpBean() (per bean) [Inject] processed (per bean) [EventHandler] processed (per bean) [Dispatcher] processed (per bean) Default custom metadata processed (per bean) [PostConstruct] processed Initialize event Creation complete event
Added to stage event Display objects in the display list are processed (see table below) Application complete event Update complete event
The following table shows the steps that Flex and Swiz will go through when a new display object is set up: Type Flex Flex Flex Flex Flex Flex Flex Flex Flex Flex Flex Flex Flex Flex Swiz Swiz Swiz Swiz Swiz Swiz Swiz Flex Step Invalidation Property bindings Preinitialize Create children Initialize event Commit properties Resize Render Measure Set actual size Update display list Creation complete event Added event Added to stage event beanFactory.setUpBean() [Inject] processed [EventHandler] processed [Dispatcher] processed Default custom metadata processed [PostConstruct] processed [ViewAdded] processed Update complete event
The following table shows the steps that Flex and Swiz will go through when a display object is torn down: Type Flex Flex Swiz Swiz Swiz Swiz Step Removed event Removed from stage event [PreDestroy] processed [Inject] tear down [EventHandler] tear down [Dispatcher] tear down
Swiz Swiz
Event Handling
Page Contents:
Dispatching Events Handling Events with [EventHandler] Event Handling Using Class and Constant Names Event Handling Using Event Type Value [EventHandler] tag options properties attribute Other attributes Handling Multiple Events from a Single Method
Swiz offers features to assist with both the dispatching and handling of events within an application. The following sections explain these features in detail.
Dispatching Events
Application events typically fall into two categories: those dispatched from children of DisplayObject, and those dispatched from non-UI objects. Swiz makes it easy to handle both kinds of events in a standard way. Dispatching events from UI components is done using the standard dispatchEvent() method that DisplayObject provides. If you dispatch events that bubble, Swiz can listen for these events in the root container and invoke event handlers to handle these events. Dispatching events from non-UI component beans is done using an event dispatcher provided by Swiz. There are two ways to instruct Swiz to inject a dispatcher. The most common way is to use the [Dispatcher] metadata tag. Swiz will automatically inject the dispatcher into a public property decorated with this metadata.
[Dispatcher] public var dispatcher:IEventDispatcher;
The other option is to implement the Swiz IDispatcherAware interface. Since implementing this interface creates a direct dependency on the Swiz framework, it should be avoided if possible. Once the dispatcher is injected, you can use it to dispatch events that can be handled using metadata-based event handlers. Event handlers are described in the next section.
In situations where you have two or more Swiz instances in a parent/child relationship, you can exercise some additional control over how events are dispatched and handled. See the section on Module Support for more information.
tags are defined on methods and must specify the event to be handled. The event can be specified using the string value of the event's type property, or by using the class and constant names of the event.
Methods decorated with [EventHandler] must be public, since the AVM provides no mechanism for invoking non-public methods directly.
If you wish to use event constants in your [EventHandler] metadata, this is the required approach. There are two two reasons Swiz requires this. First, it allows Swiz can verify the event class exists and has a constant with the provided name. Second, it allows Swiz to process different events that have the same string literal type by matching on the class and constant name instead of the value. This syntax assumes you have a subclass of Event named UserEvent, and that you have added "com.foo.events" to the eventPackages property of SwizConfig.
If you do not specify any eventPackages, you can provide the fully qualified path in the [EventHandler] tag:
Good for quick prototyping but lacks error checking and could suffer from collisions. Not recommended.
[EventHandler( event="addUser" )] public function handleAddUserEvent( event:UserEvent ):void { // do stuff }
properties attribute
You can also specify a comma delimited list of property names in the tag to have the matching properties pulled off of the event and passed into your method. This can be useful for allowing methods to be defined in a more semantic manner to mask the fact that they are event listeners. As
of version 1.3, you can use property chains as well, i.e. user.permissions.
Other attributes
Since [EventHandler] causes Swiz to create an event listener behind the scenes, it also supports some related attributes. useCapture, priority, stopPropagation and stopImmediatePropagation are all supported, and Swiz will handle their implementation for you. useCapture and priority map directly to the useCapture and priority parameters of the resulting listener. Setting stopPropagation or stopImmediatePropagation to "true" will cause Swiz to automatically call the corresponding methods on the event after your method executes.
Additionally, you can handle any event type of a given event using a wildcard:
[EventHandler( event="UserEvent.*", properties="user" )] public function handleUserEvents( user:User ):void { // do stuff }
Service Layer
Interacting with remote services is a feature found in almost every RIA created. Because of this, Swiz has made it a priority to streamline and enhance this aspect of development. There are four primary classes you will interact with in the service layer of your Swiz application.
ServiceHelper
The ServiceHelper class is probably the most common class you will use in the service layer of your Swiz application. ServiceHelper is a class that simplifies and improves your interactions with service classes that return an AsyncToken. This includes RemoteObject, HTTPService and WebService. These services will typically be defined as beans and injected into one of your own classes. ServiceHelper's executeServiceCall() method is a concise way to call a service, define handlers for the call and specify objects to be passed to your result handler. The following example shows a simple service call as it looks when using ServiceHelper. In this example the ServiceHelper has been defined as a bean and injected, but you can instantiate your own instance if you prefer. Also notice that you are able to define an array of parameters that will be passed to the result handler when the service returns. This can be very useful for maintaining context between calls and simplifying your result handler code. The first parameter of executeServiceCall() is an AsyncToken so we simply perform the RemoteObject method call, which returns an AsyncToken, inline.
[Inject( "userService" )] public var ro:RemoteObject; [Inject] public var sh:ServiceHelper; public function fetchUserRoles( user:User ):void { sh.executeServiceCall( ro.fetchUserRoles( user.id ), fetchUserRoles_result, fetchUserRoles_fault, [ user ] ); } protected function fetchUserRoles_result( data:Object, user:User ):void { user.roles = data.result; } protected function fetchUserRoles_fault( info:Object ):void { // handle service fault }
URLRequestHelper
Remote calls that do not return an AsyncToken are generally implemented using the URLRequest and URLLoader classes. Swiz therefore provides the URLRequestHelper class, whose executeURLRequest() method works much like executeServiceCall(). The slight differences can be seen below, but can be summed up in that you do not directly create a URLLoader instance or call load() because Swiz handles those aspects for you, and there are extra (optional) handlers that can be defined.
[Inject] public var urh:URLRequestHelper; public function loadConfig( user:User ):void { urh.executeURLRequest( new URLRequest( "config.xml" ), loadConfig_result, loadConfig_fault, loadConfig_progress, loadConfig_httpStatus, [ user ] ); } protected function loadConfig_result( event:Event, user:User ):void { user.config = XML( URLLoader( event.target ).data ); } protected function loadConfig_fault( event:Event ):void { // will be called in response to an IOErrorEvent.IO_ERROR or SecurityErrorEvent.SECURITY_ERROR } protected function loadConfig_progress( event:Event ):void { // will be called in response to ProgressEvent.PROGRESS } protected function loadConfig_httpStatus( event:Event ):void { // will be called in response to HTTPStatusEvent.HTTP_STATUS }
Client Persistence
Swiz provides two ways to help with client persistence. In both Flex and AIR projects you can use the SharedObjectBean. In AIR projects, you can also use the EncryptedLocalStorageBean, which can be found in the Swiz Desktop Extensions project on GitHub. (The EncryptedLocalStorageBean is kept in a separate project to avoid having a framework dependency on the AIR libraries.)
SharedObjectBean
To use the SharedObjectBean, you simply declare it in a BeanProvider:
<swiz:BeanProvider xmlns:swiz="http://swiz.swizframework.org" xmlns:storage="org.swizframework.storage.*"> <storage:SharedObjectBean id="soBean" /> </swiz:BeanProvider>
Inject the instance into your model and declare a bindable getter/setter:
[Inject] public var so:ISharedObjectBean; [Bindable] public function get appIndex():int { // the second parameter is the initial value return so.getInt("appIndex", 0); } public function set appIndex(index:int):void { so.setInt("appIndex", index); }
Interfaces
Swiz provides several interfaces to enable manual control over certain features, or as an alternative to using certain metadata tags.
ISetUpValidator
Implementing this interface will instruct Swiz to invoke the allowSetUp() method of the bean before the bean is set up. If this method returns true, set up will proceed. If it returns false, Swiz will not set up the bean.
ITearDownValidator
Implementing this interface will instruct Swiz to invoke the allowTearDown() method of the bean before the bean is torn down. If this method returns true, tear down will proceed. If it returns false, Swiz will not tear down the bean.
ISetUpValidator and ITearDownValidator are primarily meant for use in view components, to allow specific views to opt out of set up or tear down. In these cases, the developer can manually control set up or tear down of the views.
IInitializing
Implementing this interface will instruct Swiz to invoke the afterPropertiesSet() method of the bean after the bean is set up.
IDisposable
Implementing this interface will instruct Swiz to invoke the destroy() method of the bean when the bean is destroyed
IDispatcherAware
Implementing this interface will instruct Swiz to inject an IEventDispatcher into the bean's dispatcher property when it is set up.
Best Practice In order to minimize coupling to the Swiz framework, using the [PostConstruct], [PreDestroy] and [Dispatcher] metadata tags should be favored over use of IInitializing, IDisposable and IDispatcherAware.
IBeanFactoryAware
Implementing this interface will instruct Swiz to inject the Swiz IBeanFactory into the bean's beanFactory property when it is set up.
ISwizAware
Implementing this interface will instruct Swiz to inject itself into the bean's swiz property when it is set up.
Advanced Topics
This section covers more advanced Swiz topics, such as the Chaining API and custom metadata.
AutowiredTestCase
One of the utility classes that is part of the Swiz library is AutowiredTestCase. As the name implies, you can use this class to help test your Swiz applications. Let's look at a sample FlexUnit 4 test that uses AutowiredTestCase. First, the class under test:
package org.swizframework.testapp.control { import org.swizframework.testapp.event.MyEvent; import flash.events.IEventDispatcher; public class MyController { [Dispatcher] public var dispatcher : IEventDispatcher; public var didSomething : Boolean = false; [EventHandler( event="MyEvent.CONTROLLER_ACTION_REQUESTED" )] public function handleAction() : void { didSomething = true; dispatcher.dispatchEvent( new MyEvent( MyEvent.CONTROLLER_ACTION_COMPLETE ) ); } } }
You can see this is a simple controller, which responds to the CONTROLLER_ACTION_REQUESTED event, updates a property, and then dispatches CONTROLLER_ACTION_COMPLETE. We want to test that the controller is responding to the correct event, property updating the property, and finally dispatching the completion event. The event itself is a simple event class:
package org.swizframework.testapp.event { import flash.events.Event; public class MyEvent extends Event { public static const CONTROLLER_ACTION_REQUESTED : String = "controllerActionRequested"; public static const CONTROLLER_ACTION_COMPLETE : String = "controllerActionComplete"; public function MyEvent(type:String) { super(type, true, false); } } }
public class MyControllerTestCase extends AutowiredTestCase { private var myController : MyController; [Before] /** * Create the object under test, create Swiz configuration, and build the Swiz context * so that it can process the test objects as beans. */ override public function constructSwizContext() : void { myController = new MyController(); swizConfig = new SwizConfig(); swizConfig.eventPackages = "org.swizframework.testapp.event.*"; beanProviders = [new BeanProvider( [myController] )]; super.constructSwizContext(); } [After] public function cleanUp():void { myController = null; } [Test(async)] public function testControllerActionRequested() : void { Assert.assertTrue( "Controller property is already true.", myController.didSomething == false ); Async.handleEvent( this, swiz.dispatcher, MyEvent.CONTROLLER_ACTION_COMPLETE, checkEvent ); swiz.dispatcher.dispatchEvent( new MyEvent( MyEvent.CONTROLLER_ACTION_REQUESTED ) ); } private function checkEvent( event : Event, passThroughData : Object ) : void { Assert.assertTrue( "Controller property was not updated.", myController.didSomething == true ); } } }
We start off setting up an instance of Swiz that the test can use. AutowiredTestCase has a method marked with [Before] metadata, so that FlexUnit runs it before each test. Next, create an instance of the class under test (MyController), then set the event packages on the SwizConfig. Finally, we place the MyController instance into a BeanProvider, so that Swiz will process it as a bean. This way, any Swiz metadata in MyController is processed. The test method dispatches a CONTROLLER_ACTION_REQUESTED event. If all goes well, the event handler in the controller should run, update the property, and then the controller will dispatch the completion event. Running the test produces a passing result. Note that because Swiz actually processes your test case itself as a bean, you can use Swiz metadata in your test if you want or need to. So injecting a dispatcher, handling an event, or testing custom metadata processors are all possible as well.
Chaining API
Page Contents:
Chaining API Overview Provided implementations Custom implementations Automatic Asynchronous Handling in Chains Go forth and chain stuff!
public interface IChain extends IEventDispatcher { function get position():int; function set position( value:int ):void; function get isComplete():Boolean; function get stopOnError():Boolean; function set stopOnError( value:Boolean ):void; function hasNext():Boolean; function stepComplete():void; function stepError():void; function addStep( step:IChainStep ):IChain; function start():void; function doProceed():void; }
Most of the methods are self explanatory, so we'll only discuss the most important one directly. The doProceed() method is where a chain does its work. It's where EventChain dispatches events, and where CommandChain executes commands. If you clicked either of those links, you may have noticed that both chain types contain very little code other than their doProceed() implementations. This is because they both extend AbstractChain, which implements the entire IChain interface besides doProceed(). It does not actually implement IChain, however, which forces custom chain types to do so and to implement doProceed(). AbstractChain does implement IChainStep though, which means chains can be nested. Also note that chains can be run in either sequence or parallel mode, with constants defined for each in the ChainType class
public interface IChainStep { function get chain():IChain; function set chain( value:IChain ):void; function get isComplete():Boolean; function complete():void; function error():void; }
IChainStep is the base interface for objects that can be part of a chain. The methods are again pretty self explanatory, but do notice the complete() and error() methods, which correspond to the stepComplete() and stepError() methods of IChain. The last interface of the Swiz chaining API is IAutonomousChainStep.
While chains will often have specific logic for executing their constituent steps, sometimes it is beneficial for a step to control its own execution. This is especially useful for constructing chains with steps of varying types.
Provided implementations
Swiz provides some useful implementations of the chaining API for you. We previously mentioned EventChain, which is exactly what it sounds like. It allows you to string together EventChainStep instances, and also supports IAutonomousChainStep instances. EventChains can be extremely useful for things like application boot strapping and making multiple remote service calls. CommandChain provides a mechanism for assembling CommandChainStep instances, as well as AsyncCommandChainStep instances (they extend CommandChainStep and implement IResponder) and implementations of IAutonomousChainStep. Here is an example of a simple EventChain that dispatches a series of events as a single unit:
public function doEventChain() : void { var eventChain : EventChain = new EventChain( dispatcher ); eventChain.addEventListener( ChainEvent.CHAIN_COMPLETE, handleChainComplete, false, 0, true ); eventChain.addStep( new EventChainStep( new UserEvent( UserEvent.USER_SCREEN_ACTIVE ) ) ); eventChain.addStep( new EventChainStep( new UserEvent( UserEvent.USER_PROCESSING_COMPLETE ) ) ); eventChain.start(); } [EventHandler( event="UserEvent.USER_SCREEN_ACTIVE" )] public function logUserScreenActive() : void { // perform logic or dispatch events when the user screen is active } [EventHandler( event="UserEvent.USER_PROCESSING_COMPLETE" )] public function logUserProcessingComplete() : void { // perform logic or dispatch events when the processing finishes } public function handleChainComplete( event : Event ) : void { // perform logic or dispatch events when the chain is complete }
EventChainSteps may optionally specify a dispatcher. If none is specified, the EventChain will assign its dispatcher to the EventChainStep before executing that step. FunctionChainStep is another useful class that implements IAutonomousChainStep and allows for deferred function invocation. Finally, note that BaseChainStep provides a base implementation of IChainStep, making your custom implementations that much easier to write.
Custom implementations
It is extremely easy to extend the chaining API to meet your specific needs. As mentioned above, creating a custom chain type essentially boils down to extending AbstractChain, adding "implements IChain" and then implementing the doProceed() method. Your doProceed() method could execute custom chain step types, log or otherwise audit chain progress or perform any other custom behavior you desire. A simple chain that relies on autonomous steps is shown below.
public class CompositeChain extends AbstractChain implements IChain { public function doProceed():void { IAutonomousChainStep( steps[ position ] ).doProceed(); } } }
Custom chain steps can be made to perform virtually any action. They will need to implement IChainStep, which is most easily done by extending BaseChainStep. If they implement IAutonomousChainStep they can be virtually self contained, and can easily be combined with other chain step types. An example of a custom chain step which sets a property on an object is shown below.
package steps { import org.swizframework.utils.chain.BaseChainStep; import org.swizframework.utils.chain.IAutonomousChainStep; public class PropertySetterStep extends BaseChainStep implements IAutonomousChainStep { public var target:Object; public var props:Object; public function PropertySetterStep( target:Object, props:Object ) { this.target = target; this.props = props; } public function doProceed():void { for( var prop:String in props ) { target[ prop ] = props[ prop ]; } complete(); } } }
3. extends AbstractAsynchronousDispatcherOperation, so you can extend this abstract base class to adapt other asynchronous operations that signal their success or failure via Events if necessary.
As noted earlier, [EventHandler] methods can simply return an AsyncToken. The [EventHandler] metadata processor will automatically adapt the AsyncToken via AsyncTokenOperation.
For example, here is an event chain that will automatically wait for a service call to complete before moving on to the next step. First, we'll see the event that will be used for the asynchronous events in the chain. Note that it extends AsynchronousEvent, which is necessary to allow the event to keep track of the chain step it is associated with:
import org.swizframework.utils.async.AsynchronousEvent; /** * By extending AsynchronousEvent (or implementing IAsynchronousEvent), the event can keep track * of the chain step it is associated with. This allows the event to participate in asynchronous * event chains. */ public class UserEvent extends AsynchronousEvent { public static const LOAD_USER : String = "UserEvent.LOAD_USER"; public static const LOAD_USER_IMAGE : String = "UserEvent.LOAD_USER_IMAGE"; public static const USER_PROCESSING_COMPLETE : String = "UserEvent.USER_PROCESSING_COMPLETE"; public function UserEvent( type : String ) { super( type, true, false ); } }
And then the code that actually builds up the chain and starts the processing:
public function doAsyncChain() : void { var eventChain : EventChain = new EventChain( dispatcher ); eventChain.addStep( new EventChainStep( new UserEvent( UserEvent.LOAD_USER ) ) ); eventChain.addStep( new EventChainStep( new UserEvent( UserEvent.USER_PROCESSING_COMPLETE ) ) ); eventChain.start(); } [EventHandler( event="UserEvent.LOAD_USER" )] /** * Since this event handler method returns an AsyncToken, Swiz automatically knows that * if the method runs as part of an event chain, it should wait until the asyncronous call * completes before proceeding to the next chain step. */ public function loadUser() : AsyncToken { return serviceHelper.executeServiceCall( service.loadUser(), handleUserLoadResult ); } private function handleUserLoadResult( event : ResultEvent ) : void { // process the result } [EventHandler( event="UserEvent.USER_PROCESSING_COMPLETE " ) public function userProcessingComplete() : void { // perform logic or dispatch events when the processing finishes }
As another example, a URL request is adapted via AsynchronousIOOperation to allow Swiz to wait for completion before the chain proceeds:
public function doURLRequestChain() : void { var eventChain : EventChain = new EventChain( dispatcher ); eventChain.addStep( new EventChainStep( new UserEvent( UserEvent.LOAD_USER_IMAGE ) ) ); eventChain.addStep( new EventChainStep( new UserEvent( UserEvent.USER_PROCESSING_COMPLETE ) ) ); eventChain.start(); } [EventHandler( event="UserEvent.LOAD_USER_IMAGE" )] /** * Load an image. Since this event handler method returns an IAsynchronousOperation, * Swiz automatically knows that if the method runs as part of an event chain, it should wait * until the operation completes before proceeding to the next chain step. */ public function loadUserImage( event : UserEvent ) : IAsynchronousOperation { var request : URLRequest = new URLRequest( targetImagePath ); var loader : URLLoader = urlRequestHelper.executeURLRequest( request, handleUserImageResult ); return new AsynchronousIOOperation( loader ); } private function handleUserImageResult( event : Event ) : void { // process the result } [EventHandler( event="UserEvent.USER_PROCESSING_COMPLETE " ) public function userProcessingComplete() : void { // perform logic or dispatch events when the processing finishes }
An [EventHandler] method can also construct an asynchronous chain and return that chain as an operation (adapting it via AsynchronousChainOperation). In that case, the original EventChainStep will not proceed until the entire child chain is complete.
Module Support
Parent and Child Swiz Instances
Swiz supports the concept of parent and child instances. Typically, this capability is used in conjunction with Modules. When you load a module that contains a Swiz instance as a child of another Swiz-based application, the framework will create a parent-child relationship between these two instances of Swiz. This happens automatically, meaning that the developer should not have to do any manual setup or configuration to use this feature. There is a sample application that demonstrates how Swiz can work with Modules at the Github repository. Here is a graphical representation of how the module support works and what capabilities it offers:
The one main issue that developers encounter related to module support has to do with Swiz metadata-based event handling. The [EventHandler] metadata tag only contains a string value representing the event class and constant name. As a result, when Swiz attempts to validate the event, you will receive an error if that event class was not compiled into the Module. This is because Swiz cannot locate the event class in the ApplicationDomain. The developer must ensure that event classes defined outside of the module are included in the compiled module. In practice, this simply means adding a variable declaration somewhere that is typed to that event. As long as an actual reference to the event type exists somewhere within the module, that class will be compiled into the module. This allows Swiz to locate the class reference and validate the event constants. Note that this is not actually a Swiz-specific issue. The compiler only includes classes that are actually referenced in an application; all others are discarded for optimization purposes.
Swiz offers some additional control over how events are dispatched and handled between parent and child Swiz instances. Most developers will probably never need to concern themselves with this fine-grained control, but it is described here for the cases where it is desired. The [Dispatcher] and [EventHandler] metadata tags allow for an optional attribute called scope, which can be set to "local" or "global". The default value for both of these attributes is "global". If a [Dispatcher] metadata tag specifies scope="local", the dispatcher for that Swiz instance is injected. When scope="global" is specified, the dispatcher for the top-level Swiz instance is injected. Similarly, if an [EventHandler] metadata tag specifies scope="local", only events dispatched through the local dispatcher are handled by the decorated method. When scope="global" is specified, events dispatched through the global dispatcher are handled. Since this may make more sense when visualized, here is a graphic depicting the local and global scopes:
Finally, add your CommandMap to one of your BeanProviders. It is not necessary to register your Command classes as beans. Their presence in the mappings is all the information Swiz needs.
<swiz:BeanProvider> <command:MyCommandMap id="myCommandMap" /> </swiz:BeanProvider>
The commands you register must implement one of two interfaces provided by Swiz. ICommand is the base interface, simply defining an execute() method.
If your Command needs access to the event that triggered it, simply implement IEventAwareCommand. Notice IEventAwareCommand extends ICommand.
public interface IEventAwareCommand extends ICommand { function set event( value:Event ):void; }
As always, make sure you've added the correct event package containing your event to the Swiz eventPackages array and that's all there is to it. Any time Swiz handles an event, the associated command(s) will be executed.
Best Practices
This section will contain a collection of Swiz best practices that will be updated on an ongoing basis.
Presentation Model
The presentation model (PM) approach is the recommended view layer architecture for Swiz. The goal is to remove any logic from the view and let the PM handle the view logic. The PM has to be declared in the BeanProvider and the recommended way is to declare it as a Prototype so the PM will only be created (instantiated) when the corresponding view is added to the stage.
<?xml version="1.0" encoding="utf-8"?> <swiz:BeanProvider xmlns:swiz="http://swiz.swizframework.org" xmlns:fx="http://ns.adobe.com/mxml/2009"> <fx:Script> <![CDATA[ import example.model.presentation.MyPresentationModel; ]]> </fx:Script> <swiz:Prototype type="{MyPresentationModel}" /> </swiz:BeanProvider>
The view gets the PM injected and binds data from it and delegates all interactions (events) to the PM.
<?xml version="1.0" encoding="utf-8"?> <s:SkinnableContainer xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" currentState="{model.currentState}"> <fx:Script> <![CDATA[ import example.model.presentation.MyPresentationModel; [Bindable] [Inject] public var model:MyPresentationModel; [PreDestroy] public function cleanUp():void { model.destroy(); } ]]> </fx:Script> <s:layout> <s:VerticalLayout /> </s:layout> <s:states> <s:State name="default" /> <s:State name="detail" /> </s:states> <s:List labelField="{model.listLabelField}" dataProvider="{model.dataProvider}" change="model.changeListIndex(event.newIndex)" /> <s:Label includeIn="detail" text="{model.selectedItem.title}" /> <s:Button includeIn="detail" label="Edit" click="model.edit()" /> </s:SkinnableContainer>
The PM is a independent unit and only gets data injected from the application layer.
package example.model.presentation { import flash.events.IEventDispatcher; import org.swizframework.events.BeanEvent; public class MyPresentationModel { [Dispatcher] public var dispatcher:IEventDispatcher; [Bindable] public var currentState:String; [Bindable] [Inject("appModel.list")] public var dataProvider:IList; [Bindable] public var selectedItem:Object; public function MyPresentationModel() { } public function changeListIndex(index:int):void { if(index != -1){ currentState = "edit"; selectedItem = dataProvider.getItemAt(index); }else{ currentState = "default"; selectedItem = null; } } public function edit():void { dispatcher.dispatchEvent(new MyEvent(MyEvent.EDIT, selectedItem)); } public function destroy():void { dispatcher.dispatchEvent( new BeanEvent( BeanEvent.TEAR_DOWN_BEAN, this ) ); } } }
Having no logic in the view itself also means that you only have to unit test your PM.
Examples
The swiz-examples repository on Github contains a number of examples. These include the Quick Start app, examples of Module integration, the Chaining API, and more. We'll keep adding more over time, too!
Release Notes
v1.3.1 Release Notes
Fixed [ViewNavigator] metadata missing from swc - The new [ViewNavigator] metadata tag was missing from the library's
ISetUpValidator and ITearDownValidator - These interfaces declare allowSetUp():Boolean and allowTearDown():Boolean methods, respectively. Implementing one or both will allow your class to opt out of the automatic set up and/or tear down (by returning false) that is configured for the rest of your application. If you want to opt back in, simply have your class return true for the appropriate method. Implicit helper class injections - If an [Inject] tag is declared requesting an IServiceHelper, ServiceHelper, IURLRequestHelper, URLRequestHelper, or MockDelegateHelper by type, Swiz will automatically create beans for the helper classes. As a user you do not need to declare the helper classes as beans. ServiceHelper::executeServiceCall() now returns the AsyncToken - Behind the scenes, executeServiceCall() creates and uses an instance of AsyncToken to wire up your result and fault handlers. This AsyncToken is now returned from the method in order to allow additional usage as needed. URLRequestHelper::executeURLRequest() now returns the URLLoader it creates - Similar to the executeServiceCall() change, Swiz now returns the URLLoader instance it uses in case you need to interact with it. URLRequestHelper now supports extra handlerArgs - All handler methods specified will receive the same handlerArgs provided to executeURLRequest(). BeanEvent refinements - BeanEvent now has four distinct types: ADD_BEAN, REMOVE_BEAN, SET_UP_BEAN and TEAR_DOWN_BEAN. ADD_BEAN will not only set up the attached object (as SET_UP_BEAN does), it will also make it available as a bean to be injected elsewhere. REMOVE_BEAN is the corresponding type, causing the attached object to be both torn down and removed from the list of available beans. Smarter [EventHandler] - The Flash Player's event listening system is, at its core, based on strings. This means that event listeners can sometimes collide if the event types they are listening for have the same string value. However, if you use the class based syntax for [EventHandler] (or [Mediate]), Swiz will now prevent these collisions. So if you have [EventHandler( "FooEvent.FOO" )] on one method and [EventHandler( "BarEvent.FOO" )] on another, Swiz will call the correct one based on the actual type of the event instance, even if both evaluate to "foo". Sweet! Better workflow for overriding processors - Needing to override the default processors for tags like [Inject], [PostConstruct] and the others is extremely rare. At the same time, we weren't happy with the cumbersome workflow for doing so. The process has been improved to simply use processor priorities as a unique list. If you provide a custom processor whose priority matches a built-in processor's, Swiz will replace the built-in processor with your own. Chaining API enhancements - This is perhaps the most significant and exciting change since the RC2 release. See the wiki for more info. http://swizframework.jira.com/wiki/display/SWIZ/Chaining+API Miscellaneous optimizations and bug fixes - There have also been countless refinements, optimizations and bug fixes in order to ensure this is a fast, solid framework. Check it out and let us know what you think! For a complete list of changes made for this release see this compare view.
heavily from Robotlegs (robotlegs.org), because their implementation was clean and in line with our goals." Look for a blog post soon showing how to use this new feature! IBeanFactory cleaned up - BeanFactory is really the heart of Swiz. It is responsible for boot strapping Swiz instances and generally managing the IoC container. We have cleaned up its IBeanFactory interface to be more consistent and predictable. BeanEvent additions - Related to the IBeanFactory upgrades, we have improved BeanEvent as well. BeanEvent has been around for a while, with constants for SET_UP_BEAN and REMOVE_BEAN. Dispatching a BeanEvent with one of these types and an object instance attached would, respectively, set up (provide the dependencies and process the metadata declared by the object) or tear down (clear out the injected dependencies) the object. Part of the IBeanFactory improvements, however, were the distinctions between bean set up and tear down, and bean addition and removal. Adding a bean makes it available for injection into other objects, removing it makes it unavailable for injection. In order to allow all of these actions to be triggered by an event, we've added ADD_BEAN and REMOVE_BEAN to BeanEvent. One last note is that adding a bean will also set up that bean, but the opposite is not true; you can set up an object without making it available for others to inject. On the other end of the lifecycle, removing a bean will always tear the bean down, but tearing down does not imply removal. Among other reasons, this is because views may be removed from the stage and re-added, causing them to be torn down and then set up again. For a complete list of changes made for this release see this compare view.
event. Dispatching BeanEvent.SET_UP_BEAN will trigger Swiz to pass an object through it's injection and mediator processors. Dispatching BeanEvent.TEAR_DOWN_BEAN will trigger Swiz to remove any mediators and injection bindings it has created. Removed setUpMarkerFunction - We briefly provided a way for users to determine whether or not a view should be wired up. This has been removed so that a proper, more robust API can be implemented in the future. [Inject( bind="false" )] - Previously, when injecting bean properties with a tag like [Inject( "myModel.someProp" )], Swiz would create a binding if the source property was bindable. We have changed the default behavior so to make you choose to create bindings if you need them. To make Swiz to create a binding for you, you must set bind to true in the metadata tag. [Inject( "myModel.someProp", bind="true" )]. In general, using Inject with bindings should be treated carefully. We would like to discourage it's overuse, particularly in situations where users are creating global variables for their applications through the Inject tag. For a complete list of changes made for this release see this compare view.
IDE Support
The SourceMate 3.0 plugin for Flash Builder adds a large number of extremely useful capabilities to Flash Builder. One of these is excellent IDE integration for the Swiz framework. Here are a few of the great features that SourceMate offers when building Swiz applications in Flash Builder.
Configuration
To start with, it provides a Swiz-specific menu for doing common things like configuring Swiz projects and creating new Beans: Swiz Menu
Swiz project configuration can usually be automatically detected. For cases where a project has an unusual setup, information can be specified manually as well: Swiz Configuration
Choosing a bean and hitting enter will automatically generate a public property with the relevant [Inject] tag. Similarly, if you type "handle" and trigger content assist, you are presented with a list of the events declared in your event packages: EventHandler Content Assist
Selecting an event and hitting enter will prompt you to decide whether you want to pass the event itself to the event handler method, or pass specific event properties: EventHandler Content Assist (Properties)
Finally, the [EventHandler] tag and handler method are generated automatically, including any selected event properties that you wish to pass: EventHandler Content Assist Code
Beans View
The Beans in use by a project can be quickly shown via the Swiz Beans view: Bean List with Injections
Notice that in addition to listing the Beans, this view also shows you exactly where each of the Beans is injected into other classes.
Events View
A similar view is available to view the events in use by your Swiz application: Event List with EventHandlers
Again, notice that in addition to showing the events, it lists each event type along with the location of all [EventHandler] metadata which references that event type. This is extremely handy for tracing event flow through an application.
Wrapping Up
The SourceMate team has done a great job with this support for Swiz, and has been very receptive to feedback. We hope that as more people use the tool and take advantage of its Swiz support, even more cool features will be added in the future. They have a free trial, so kick the tires and test out the Swiz support yourself (along with the impressive list of non-Swiz enhancements that SourceMate brings to Flash Builder).
Migration Guide
Tips for migrating from earlier versions
You might also note that several of the properties on Swiz have changed: the strict property has been renamed validateEventTypes, and now defaults to true so in most cases it can be removed mediateBubbledEvents has been removed since Swiz 1.0 mediates bubbled events automatically the beanLoaders property has been renamed to beanProviders, and has been moved to the Swiz class the eventPackages and viewPackages now accept package paths in both the format myapp.events and myapp.events.*. Note that you still need to specify child packages separately even if you use the .* syntax. the injectionEvent property has been renamed setUpEventType, and now defaults to Event.ADDED_TO_STAGE instead of Event.PREINITIALIZE Full details on configuration and the default values for 1.0 can be found in the Configuration section. The BeanLoader class has been deprecated in favor of BeanProvider. BeanLoader will still work in 1.0, but it is recommended that you update your code to BeanProvider. In addition, a central dispatcher property was available within BeanLoader in 0.6.4. This property has been removed in 1.0. If a bean or prototype bean requires a dispatcher from Swiz, this is now accomplished using the [Dispatcher] metadata tag:
Other changes
the [Autowire] metadata tag has been deprecated in favor of [Inject]. [Autowire] will still function in 1.0 but it is recommended that you update your code to use [Inject].
create individual patches in Git to target specific bugs. If you've gone the route of updating a fork (recommended), all you need to do is submit a pull request via GitHub. The next step is to let us know by sending the following information to the Swiz team: URL for your forked repository / or Git patch Detailed description of your intent What did you do? Why did you do it? Test Cases / Unit Tests - While test cases are optional but they will assist in getting your changes committed. If you provide unit tests, you're awesome.
Presentations
Space to house slide decks and notes from any Swiz presentations given at conferences, user groups, etc. BFlex - October 25, 2009 - Ben Clinkinbeard 360Flex BOF - March 8, 2010 - Snke Rohde & John Yanarella cf.Objective() - April 23, 2010 - Brian Kotek
FAQ
Is there a mailing list where I can get help from the Swiz community? If I am exporting a release build for a Swiz project, what compiler arguments do I need to retain the custom metadata? If I am building the Swiz framework from the source code, what compiler arguments do I need? I try to access my injected property during creationComplete but the reference is still null? What versions of Flex are supported for Swiz? Does Swiz work with Adobe AIR? I have dispatched an event from a view but the [EventHandler] is not invoked? How can I cause a Prototype bean to be destroyed when a view is destroyed? Why aren't new PopUp or AIR windows processed by Swiz (especially if created from within a Module)? Why aren't bubbling events dispatched from a PopUp handled by Swiz? Where is my application logic starting point? How should I dispatch events from bean classes? Why did my view not get injections processed? I defined a WebService object in my Beans.mxml, but my web service calls don't seem to work. What's wrong? Why do I get an error when using Swiz in a Flash Builder project when I use Design View?
Is there a mailing list where I can get help from the Swiz community?
Absolutely, you can find the Swiz group at http://groups.google.com/group/swiz-framework.
If I am exporting a release build for a Swiz project, what compiler arguments do I need to retain the custom metadata?
Because the Swiz swc is built with arguments to instruct the compiler to retain the Swiz metadata, you shouldn't normally need to do this yourself. However, if the metadata isn't retained for some reason, you can add this to the compiler arguments for your project:
-keep-as3-metadata+=Inject,Autowire,EventHandler,Mediate,Dispatcher,PostConstruct,PreDestroy,ViewAdded,ViewRemoved,View
If I am building the Swiz framework from the source code, what compiler arguments do I need?
When compiling for the Flex 3.x SDK, use -define+=CONFIG::flex4,false -define+=CONFIG::flex3,true. Be sure you only compile the /src directory into the library. Trying to compile the other packages, such as /tests, will result in library compile errors. You will also want to mark the metadata.xml and manifest.xml files as Assets in your Build Path panel. Lastly, using the Ant build script will take care of all of these things for you, so that is the easiest way to build Swiz from source.
I try to access my injected property during creationComplete but the reference is still null?
Swiz injects dependencies when the view's addedToStage event is dispatched, which is after creationComplete. Inject to a setter function to have direct access to the injected reference, or move your logic into a method decorated with [PostConstruct].
I have dispatched an event from a view but the [EventHandler] is not invoked?
Ensure that the event handler method is public, and that the bubbles property of the event is true.
Why aren't new PopUp or AIR windows processed by Swiz (especially if created from within a Module)?
Because PopUps and NativeWindows are not part of the standard display list, Swiz has no way to know which Swiz instance is the owner of the window. To deal with this, you can have the class that creates the window implement ISwizAware. Once you have the correct Swiz instance injected, you can call the registerWindow() method on that Swiz instance to associate that window with the specified instance of Swiz. The window can then be processed correctly.
up. In this case, you can use an event handler method such as [EventHandler( "mx.events.FlexEvent.APPLICATION_COMPLETE" )]. This will mediate the applicationComplete event, and only kick off processing once all of the beans have been set up.
I defined a WebService object in my Beans.mxml, but my web service calls don't seem to work. What's wrong?
The Flex WebService class actually has two implementations, one for MXML and one for AS3. If you define a WebService within a UIComponent, the Flex framework automatically triggers a load of the web service's WSDL in the UIComponent's creationComplete() handler. However, the AS3 version does not (it has no creationComplete() handler). Because the BeanProvider is not a UIComponent, this automatic loading of the WSDL is not done by the Flex framework. As a result, you must call loadWSDL() on your web service before you can use it, because you are actually using the WebService's AS3 implementation.
Why do I get an error when using Swiz in a Flash Builder project when I use Design View?
Researching this seems to indicate that it can happen with any .swc where the .swc was compiled using a version of the SDK other than the version used by the project. Since there are so many possible SDK versions, creating separate versions of Swiz for all of them is not possible. If you need to use Design View, and you're using an SDK other than the one the framework was compiled with, we recommend that you download the source code, set up the framework as a library project, and add the library project to your application project. This should resolve the Design View error.