Basics-Introducing SproutCore MVC


SproutCore applications follow a classic design pattern that will help you organize your application code called "Model, View, Controller", or MVC.  MVC applications divide their code into three distinct layers:

 

 

More specifically, the SproutCore MVC is based loosely on the way Cocoa implements MVC.  To really understand SproutCore's MVC model, read the Cocoa Model-View-Controller Documentation as well.

 

MVC+SDR

 

Although MVC has been a very successful design pattern for client applications for over 20 years, SproutCore applications run in the web browser, which presents some unique challenges.  To support this model, SproutCore extends the traditional MVC architecture by adding three more layers to your application: Server Interface, Display, and Responders. 

 

 

The display layer in particular is important to understand because it deviates significantly from most desktop platform frameworks.  Rather than painting and resizing your display directly, SproutCore views merely render HTML which they then hand to the web browser.  The web browser is then responsible for repainting, resizing, and sometimes even animating the content on its own.  Since browsers have been optimized for the last 15 years to display and update HTML very quickly, you can get much better performance by learning to rely on these abilities to render your application.

 

The figure below shows you the main parts of your application in the MVC+DSR architecture and how they interact:

 

MVC DS

 

The following sections will described each of the layers in more detail.  If you understand these principles it will make the rest of SproutCore much easier to understand.

 

The Model Layer

 

  1. Most of your business logic lives in the model layer.  
    1. You define classes here that represent the types of data you will work with like Contact, Event, Photo, etc.
    2. When your app runs, you will create instances of these classes to manage the actual data in your application.  
    3. These model objects can have relationships to one another.  For example, an Event may have attendees, which is an array of one or more Contact objects.  
    4. These classes and their relationships to one another is called a schema.
  2. You use SproutCore's Datastore framework to describe your schema.  
    1. The schema will handle automatically mapping JSON data you send and receive from your server into JavaScript objects that you can use to manipulate your content.
    2. The datastore will also manage the lifecycle of these objects, controlling when they are created, modified and deleted, and managing propogating changes throughout your app.
  3. You implement most of the "business logic" in your application by adding methods and properties to your model classes.
    1. For example, if you need to be able to send an invite to attendees of an event, you might add a method called "sendInvitations()" to  the Event class that will do this basic logic for you.
  4. The important thing is that models should generally be able to run even if you were not in a web browser.
    1. They don't display anything, manipulate the DOM, or respond to events.
    2. If you are implementing both a desktop and mobile version of your app, you should be able to share your model layer between the two applications.
  5. The best way to think of your model layer is as a directed object graph
    1. It's a collection of objects connected by their relationships.  The structure of this object graph represents the structure of your data rather than the structure of your application.
    2. Generally your model objects should be designed fairly independent of one another; interacting mostly through their relationships.

 

The View Layer

 

  1. Views control the display of your application and are the first to respond to user events such as mouse clicks, touches, or key presses.
    1. Usually views have properties exposed that control their appearance or reflect their current state.
    2. Whenever you change one of these properties, the view will automatically update the display to reflect that change.
    3. Likewise, whenever you click a mouse, touch the screen, or press a key, a view will usually get this event first and in turn update one of its properties, call methods on delegate or make other changes.
  2. SproutCore comes with a fairly complete set of views built into its Foundation, Desktop and Mobile frameworks.  Although the number of views are small compared to other frameworks, they are very flexible and can often be adapted to your needs.
    1. The Foundation framework contains a few basic views that are equally useful on both mobile and desktop platforms, such as the core SC.View class, labels and buttons.
    2. The Desktop framework contains additional views useful for building desktop-style applications (that is, complex applications meant to be viewed on a larger screen).
    3. The Mobile framework contains views useful for building applications that run on an iPhone, Android or Palm Pre device.  Currently this framework is pretty bare, but we would like to build up a more complete set of controls here over time.
    4. You will usually only use Foundation and either Desktop or Mobile in a single application, but not both.  If you plan to build an app for both desktop and mobile devices, you will often create two applications - one for each target.
  3. If you need to customize view behavior or build a specialized view, you can always create your own custom views.  
  4. Usually your views will be structured like a tree, with the parent views managing their child views.  
    1. Think of a parent view and its child views as "aggregates".  For example, a custom dialog view is responsible for managing the layout and configuration of the controls inside of it.  

 

The Controller Layer

 

  1. Controllers connect your models to your views. 
    1. The primary purpose of a controller is to shuffle data between your model and your views. 
    2. Most of the time this is fairly simple code, but sometimes it will be more complex if your model does not fit exactly the way views need to consume it.
    3. For example, if you have an Event with attendees and you want to display the attendees in groups based on whether they have responded to invitations or not, you might need to write some extra controller code to reorder the attendees for display in this way.
  2. In SproutCore, Controllers depend heavily on computed properties and bindings to handle relaying data.  There are also some built-in classes in the Foundation framework to help you.
    1. Bindings connect a property on one object to a property on another object.  Changing the property value on one object will automatically propagate to the other object, and visa versa.
    2. Usually you bind properties on your controller objects to properties on your views.  You can then write methods that will modify the properties on the controllers, automatically updating your views.
    3. This design allows you to evolve your  view layer over time without having to rewrite much of your application code since the rest of your application only talks to your controllers.  
    4. You can even swap out your view layer entirely if you want, simply by changing the bindings. 
      1. This is key to creating mobile and desktop versions of your app.  
  3. Controllers in SproutCore act like fixed "anchor points" in your application where your model and views can meet.  

 

The Responder Layer

 

  1. Responder layer controls the overall state of your application. 
    1. For example, your app may show one kind of UI when your data is still loading, another UI when the user is browsing data and another UI when editing data.
    2. The responder layer is where you implement the code to transition between these states and detect what actions the user can perform in each state.
  2. SproutCore provides a "responder chain" that you can implement to manage the state changes in your application.
    1. Responders are normally used in the UI to route keyboard and mouse events, but they have been adapted for use in responding to general application "actions" as well.
    2. Usually the code that manages transitioning between these states and detecting what actions the user can perform in each state is messy and difficult to maintain.  It's often where you find the most bugs and edge cases.
    3. SproutCore responders provide a clean consistent way of handling this code that eliminates the spaghetti.
  3. You can model your responder chain using statecharts

 

Building Your Own MVC+SDR Application

 

SproutCore provides classes to help you write code at all five levels of your applications.  It also provides a facility – Property Bindings – that makes it easy to connect the various layers of your application without writing a lot of glue code.

 

Most of the time you will write an application by starting with these classes and "filling in the blanks" with features and functionality specific to each layer.  Then you can use SproutCore Bindings to connect the various layers together to form a complete application.

 

You will have the most success if you get used to writing each layer more or less independent of the others and then using bindings to tie them together loosely.  This way you can easily unit tests and evolve each layer without impacting the others.

 

Desktop MVC vs Server MVC

 

Many developers are first exposed to the MVC model when they write web applications using server-side frameworks just as Java Struts, Ruby on Rails or Django.  While these frameworks do use MVC, their application of this pattern differs significantly from the way it is used on the desktop and in SproutCore.  

 

A typical server-side MVC framework works by having controllers, which implement your business logic, receive incoming requests.  To service this request, the controller will use the model layer to collect data and then use "views" (which are usually HTML templates of some kind) to generate a response.  

 

On the desktop, however, keyboard or mouse events trigger code in the view layer instead of the controller layer.  This code will update the view state, which will in turn relay to the controller objects which may themselves relay further changes to the model objects.

 

Because desktop applications rely more on all three layers to split out logic, you will often find yourself writing more intelligent view classes than you might with a server framework.  You will also need to think more about the views as dynamic components that can change over time instead of static templates that render once per event.

 

TODO: Add more detail to this page explaining MVC and how it can be used in your application.  Most of the content on the Cocoa MVC page (newer link: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaDesignPatterns/CocoaDesignPatterns.html#//apple_ref/doc/uid/TP40002974-CH6-SW1) is relevant to SproutCore, it would be nice to have it re-written here in a SC-specific way.

 

Moving On

Sproutcore Objects and Namespaces »