
PureMVC + Your Favorite Framework
bring in the events!
For all the PureMVC users out there, those who can't sneeze without sending a notification, here's a tutorial/test on how to hook up PureMVC with your iOS (and Android) application and let it manage your stuff.
But first... why?
Well, good question. If you already think PureMVC is the greatest thing since piracy, you don't need convincing. And of course if you build "real" apps or gadgets and not just games, PureMVC is THE thing to have at your side, I'm telling you.
For those who don't know PureMVC, the acronym stands for A-Whole-Lot-Of-Extra-Steps-I-Need-To-Implement-Before-I-Get-Things-Rolling-But-Once-That's-Done-The-Pay-Off-Usually-Is-Worth-It
For those who don't care... well, you are staring at the greatest pornographic library in the world, go have fun!
If you've been to the PureMVC website recently you will know they have ports for every imaginable language/script you can think of (take that RobotLegs!). So why not use them?
Seriously, what do I get from it?
An MVC framework. Plus warnings.
In mobile devices, the operating system will warn your application about all sorts of things: I'm done loading, I'm ready, I'm pausing, I'm leaving... So it would be nice to be able to take these warnings to your entire application through event listeners. PureMVC can help with that.
Plus, it is a pretty neat way to organize Screens, if you are using Screen objects to organize your game, that is. Also, if your game has lots of screens and it needs to load and dispose different sprite sheets each time a new screen is created.
It is also wonderful at managing non persistent data. Particularly the type of data that gets updated by the user: scores, game state, game settings, inventory... The type of data that needs to be saved just before your application goes poof!
In other words if you need a lot of data management, an MVC framework won't hurt.
How to get the thing working?
I will show you how to use it with iOS but I will also post the code sample on how to use it with Android.
I posted a gigantic tutorial on how to use PureMVC, a long time ago, if you think you need it, you can get it here. But I will assume you know it already for this post.
The Sample Application
The Facade
#import "ApplicationFacade.h"
#import "StartupCommand.h"
#import "ShowHighScoresCommand.h"
@implementation ApplicationFacade
-(void)startup:(id)app {
[self sendNotification:STARTUP body:app];
}
+(ApplicationFacade *)getInstance {
return (ApplicationFacade *)[super getInstance];
}
-(void)initializeController {
[super initializeController];
[self registerCommand:STARTUP commandClassRef:[StartupCommand class]];
}
-(void) pause {
[self sendNotification:APP_PAUSE];
}
-(void) resume {
[self sendNotification:APP_RESUME];
}
-(void) memoryWarning {
[self sendNotification:APP_MEMORYWARNING];
}
-(void) stopAnimation {
[self sendNotification:APP_STOPANIMATION];
}
-(void) startAnimation {
[self sendNotification:APP_STARTANIMATION];
}
-(void) terminate {
[self sendNotification:APP_WILLTERMINATE];
}
@end
The Facade object should be a Singleton and its main functions are:
- It initializes the Framework by registering the first Command object, usually called StartupCommand. As a matter of fact all Commands used in the application are usually registered in the Facade object inside initializeController.
- The Facade will have a startup method, that receives an instance of the application's main entry point. This will be wrapped inside a mediator, usually as a result of firing the Notification registered with the StartupCommand, in this case the string STARTUP.
- Then comes the extra stuff. I created methods in the Facade to respond to each of the OS main notifications.
I decided to reference the Facade from inside the AppDelegate object this way:
The ApplicationDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
//create your app here
//the app will create the Facade
}
- (void)applicationWillResignActive:(UIApplication *)application
{
[[ApplicationFacade getInstance] pause];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[ApplicationFacade getInstance] resume];
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
[[ApplicationFacade getInstance] memoryWarning];
}
-(void) applicationDidEnterBackground:(UIApplication*)application {
[[ApplicationFacade getInstance] stopAnimation];
}
-(void) applicationWillEnterForeground:(UIApplication*)application {
[[ApplicationFacade getInstance] startAnimation];
}
- (void)applicationWillTerminate:(UIApplication *)application {
[[ApplicationFacade getInstance] terminate];
}
Of course at the moment the AppDelegate object is created there is no Facade. I never reference the facade from applicationDidFinishLaunching as you noticed. The facade gets created inside the App's init method. But for all the subsequent AppDelegate events the Facade should exist already. But if you think this is weird in any way, you can instead call the app created inside applicationDidFinishLaunching and let it deal with the facade and the notifications (It is how it's done in LibGDX as you will see.) But it's up to you. The result is the same.
Inside the App's Init logic
- (id)init
{
if ((self = [super init]))
{
NSLog(@"Init AmazingGame");
//Game initialization logic here
[[ApplicationFacade getInstance] startup:self];
}
return self;
}
As I said before, you could choose to have the pause, resume, terminate... methods here, inside your application's main class.
The facade then starts up with a reference to the game's main class. This will fire the STARTUP notification and carry that game object in its body. That notification is registered to the StartupCommand which will do the following:
The StartupCommand
#import "StartupCommand.h" #import "ApplicationFacade.h" #import "AmazingGame.h" #import "AmazingGameMediator.h" @implementation StartupCommand -(void)execute:(id)notification { AmazingGame *game = [notification body]; [facade registerMediator:[AmazingGameMediator withViewComponent:game]]; [self sendNotification:INIT_GAME body:nil]; } @end
It creates the first mediator, wrapping it around the game object. It then fires the notification that will result in the creation of all other mediators and game elements. But that is logic specific to this application and from here on end you can choose to manage the application differently.
But one key thing happens in the registerMediator method.
If you download the examples from the PureMVC website, particularly the Java and Objective-C examples, you will see that the developers chose to hook up the mediator with its view component AND vice-versa.
In all my previous examples of how to use PureMVC with Flash, I used events to communicate between view component and mediator. So if the user presses the login button inside a view component, this will fire an event, and the mediator is listening to it. So in the way I used to do this, the View Component knows nothing of its mediator. The same doesn't happen in the examples, and I think this is by far the best way.
I find the method used in the Java version a bit nasty: the reference to the mediator is passed to the view component in its constructor.
public MainScreen(MainScreenMediator mediator) public LoginScreen(LoginScreenMediator mediator) public SplashScreen(SplashScreenMediator mediator)
I found the method used in the Objective-C example a little more interesting. The developer there used a method from the Mediator class called onRegister which is called whenever a new mediator is registered. The developer hooked up the view component with its mediator there. Of course he used Objective-C own logic on delegates and protocols to do that, and I wanted something I could use both in Objective-C and Java. So this is what I did:
Inside every Mediator class
-(void) onRegister {
[(Screen *)self.viewComponent setMediator:self];
}
In this example most of my mediators wrap Screen objects as its view component. And every view component object will have a property called _mediator which stores the reference being passed here.
Now a view component can send notifications to the framework and talk straight to its mediator.
As an example here is how user input is processed in the HomeScreen (Sparrow version)
HomeScreen
-(void) onTouch:(SPTouchEvent *) event {
SPTouch *touch = [[event touchesWithTarget:self andPhase:SPTouchPhaseEnded] anyObject];
if (touch) {
SPPoint * tap = [touch locationInSpace:self];
SPRectangle * _highScoresBounds = [_highScoresBtn bounds];
SPRectangle * _howToBounds = [_howToPlayBtn bounds];
SPRectangle * _playBounds = [_playBtn bounds];
if ([_highScoresBounds containsPoint:tap]) {
[_mediator sendNotification:SHOW_HIGHSCORES_SCREEN];
} else if ([_howToBounds containsPoint:tap]) {
[_mediator sendNotification:SHOW_HOWTO_SCREEN];
} else if ([_playBounds containsPoint:tap]) {
[_mediator sendNotification:SHOW_LEVELSELECT_SCREEN];
}
}
}
Notice the calls to _mediator. I could of course create specific methods inside the HomeScreenMediator to handle calls from its view component, and not simply let the view talk to the whole framework by sending notifications itself. But that's party pooper behavior!
And here is how the screens are managed, in AmazingGameMediator:
Inside AmazingGameMediator
-(NSArray *)listNotificationInterests {
return [NSArray arrayWithObjects:INIT_GAME, SHOW_HOME_SCREEN, SHOW_HOWTO_SCREEN, SHOW_HIGHSCORES_SCREEN, SHOW_LEVELSELECT_SCREEN, SHOW_GAME_SCREEN, nil];
}
-(void)handleNotification:(id)notification {
if ([[notification name] isEqualToString:INIT_GAME]) {
[self initGame];
} else if ([[notification name] isEqualToString:SHOW_HOME_SCREEN]) {
[self showScreen:(Mediator *) [facade retrieveMediator:[HomeScreenMediator NAME]]];
} else if ([[notification name] isEqualToString:SHOW_HOWTO_SCREEN]) {
[self showScreen:(Mediator *) [facade retrieveMediator:[HowToPlayScreenMediator NAME]]];
} else if ([[notification name] isEqualToString:SHOW_HIGHSCORES_SCREEN]) {
[self showScreen:(Mediator *) [facade retrieveMediator:[HighScoresScreenMediator NAME]]];
} else if ([[notification name] isEqualToString:SHOW_LEVELSELECT_SCREEN]) {
[self showScreen:(Mediator *) [facade retrieveMediator:[LevelSelectScreenMediator NAME]]];
} else if ([[notification name] isEqualToString:SHOW_GAME_SCREEN]) {
[self showScreen:(Mediator *) [facade retrieveMediator:[GameScreenMediator NAME]]];
}
}
-(void) initGame {
NSLog(@"Init All MEDIATORS");
//create screen objects
HomeScreen * homeScreen = [[[HomeScreen alloc] initWithGame:self.viewComponent] autorelease];
[facade registerMediator:[HomeScreenMediator withViewComponent:homeScreen]];
HowToPlayScreen * howToPlay = [[[HowToPlayScreen alloc] initWithGame:self.viewComponent] autorelease];
[facade registerMediator:[HowToPlayScreenMediator withViewComponent:howToPlay]];
HighScoresScreen * highScores = [[[HighScoresScreen alloc] initWithGame:self.viewComponent] autorelease];
[facade registerMediator:[HighScoresScreenMediator withViewComponent:highScores]];
LevelSelectScreen * levelSelect = [[[LevelSelectScreen alloc] initWithGame:self.viewComponent] autorelease];
[facade registerMediator:[LevelSelectScreenMediator withViewComponent:levelSelect]];
GameScreen * gameScreen = [[[GameScreen alloc] initWithGame:self.viewComponent] autorelease];
[facade registerMediator:[GameScreenMediator withViewComponent:gameScreen]];
//show home screen *** INIT THE GAME ****
[self sendNotification:SHOW_HOME_SCREEN];
}
-(void) showScreen:(Mediator *) mediator {
Screen * screen = mediator.viewComponent;
[(AmazingGame *) self.viewComponent setScreen:screen];
}
-(void) onRegister {
[(AmazingGame *) self.viewComponent setMediator:self];
}
When a screen is replaced, the AmazingGameMediator receives a notification that gets handled by calling the showScreen method. This method receives the mediator of the screen I want to display now. So it can get from the mediator the actual screen object. It passes it to its own component, the AmazingGame object, which will take the necessary steps to replace the current screen with the new one.
And that's it really. From here on end, if you decide to add Proxies and more Commands, you can check the sample projects from the PureMVC site.
I know that at a first glance it feels like a lot of work for nothing. But there is a moment with PureMVC, when you are well into the development process, when it suddenly seems like things are happening as if by magic. You send one notification to a MacroCommand and boom, everything falls into place all at once. It's quite beautiful, and in that moment you are very happy that you chose to use the damn thing.
You can download the source code for The Super Amazing Game which uses PureMVC with:
- Sparrow
- LibGDX
LibGDX already does a great job at hooking up your application to the OS notifications. Especially if you use its Game-Screen model. This is because LibGDX allows, and indeed forces, your application to stay a lot closer to its entry point, since you must run your update and rendering logic from there, and so you are always that much closer to the notifications. But you still might find it helpful to have PureMVC.
Oh, and one more thing for the Java version. Depending on which version of PureMVC you use you will need to run the registerCommand method in one of these two ways:
registerCommand(STARTUP, StartupCommand.class); registerCommand (STARTUP, new StartupCommand());
The example uses the StartupCommand but the same thing would have to be done with every call to registerCommand.

