MVC in Appcelerator

Regardless of if you actually plan on delivering two distinct apps (normal and HD) or not, you’ll want to architect your application in such a way that you can maximize the sharing of code.  This means using Object-oriented Javascript and a true MVC (Model-View-Controller) paradigm in your code organization.

First we’ll start off with app organization. This seems simple, but it isn’t.

The first thing an Appcelerator app does when loading your app is to run “app.js”.  So our app.js looks something like this:

1
2
3
4
5
var window = Ti.UI.createWindow(
{
  url: "views/main.js"
});
window.open();

What this does is open up a new window, with a new execution context in the views/main.js file.  This is great, because when we include further javascript files, they will all be loaded from the 2nd-level directory (i.e. views, controllers, include, etc.).  If we had stayed within the first execution context, we wouldn’t have been able to organize our files so nicely, since everything would have to be loaded from the base directory. You can read more on execution contexts here.

Object-oriented Architecture

In modern programming app design, we want to write and maintain as little code as possible, which means re-using code wherever possible.  In order to do this with Appcelerator, we need to use good inheritance and OO design principles.

First, we should create a base controller and view class. This uses a custom inheritance scheme by John Resig .

common/controller.js

1
2
3
4
5
6
7
8
9
Ti.include("../include/inheritance.js");
 
var Controller = Class.extend({
    init: function(win)
    {
        this.win = win;
        win.controllerClass = this;
    }
});


common/view.js

1
2
3
4
5
6
7
8
9
10
11
Ti.include("../include/inheritance.js");
 
var View = Class.extend({
    init: function(win, controller)
    {
        this.win = win;
        this.controller = controller;
 
        win.viewClass = this;
    }
});

Now, we can create our real controllers and views:

controllers/main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Ti.include("../include/inheritance.js");
Ti.include("../include/db.js");
Ti.include("../common/controller.js");
Ti.include("../views/main.js");
 
var MainController = Controller.extend({
    init: function(win) {
        this._super(win);
 
        // Create the view for this controller
        this.view = new MainView(win, this);
    }
});
 
(function() {
    var win = Titanium.UI.currentWindow;
 
    // Create our controller for this window.
    new MainController(win);
})();

As you can see, now we’ve got a main controller, which also instantiates a view.   Now, the view can handle all layout code, and the controller can respond to those events, and affect the view appropriately.  This works especially well when designing Universal Apps, as we can Design two views, a iPhoneMainView, and iPadMainView, and instantiate the appropriate one.

Now for the view code:
controllers/main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Ti.include("../include/inheritance.js");
Ti.include("../include/db.js");
Ti.include("../common/view.js");
 
var MainView = View.extend({
    init: function(win, controller) {
        this._super(win, controller);
 
        this.layout();
    },
 
    layout: function() {
       // Your layout code.  Ti.UI.createLabel({}), etc.;
    }
 
});

I’ve created a Github project called Appcelerator-On-Rails to show this code. I use this template in all my Appcelerator projects and add to it when necessary.

Happy iPhone coding!

7 Comments

  • By magic_hat, June 1, 2011 @ 12:20 pm

    Scott, this is awesome. I’ve been looking for something like this for months.

    Can you point me to examples of projects using this? Would be really helpful to see the code in use…

  • By Scott, June 1, 2011 @ 12:22 pm

    I will try to post some example code in the next few days.

  • By magic_hat, June 8, 2011 @ 1:07 pm

    that would great! Thanks.

  • By Scott, June 13, 2011 @ 12:51 am

    @magic_hat – check out https://github.com/smontgomerie/Appcelerator-MVC-Demo for an example project.

  • By John, August 4, 2011 @ 2:01 am

    Hi,

    Nice implementation!

    Is there a way to create global variables? E.g. I want to access a certain value across all my controllers and views.

    Thanks.

  • By Scott, August 4, 2011 @ 9:06 am

    @John – Because of the way Appcelerator handles execution contexts, you have to pass variables from one window to the next. For example, in building a navigation controller-based app, I typically pass a pointer to the nav controller to any windows I add to the stack.

    That being said, there are more hacky ways to do it, but I’ve never had much luck with this method. Check out:
    http://developer.appcelerator.com/question/1651/global-variable

Other Links to this Post

  1. Structured MVC for Appcelerator — September 8, 2011 @ 12:08 pm

RSS feed for comments on this post. TrackBack URI

Leave a comment

WordPress Themes