On the future of SproutCore

It’s been nearly four months since SproutCore launched to the public at WWDC and we couldn’t be happier with the results.  18,000 developers have installed SproutCore (sudo gem install sproutcore ftw), nearly 1,000 developers have joined the mailing list, and dozens of projects are underway at companies around the world.  One additional one has already gone public (OtherInbox).

During this time the developers working on SproutCore haven’t stood still either.  150 tickets closed, some major new features, and enhancements for Windows, IE7, Chrome, and others.  Many of the changes we’ve applied have come from you, the community.  In fact, over 20 people have contributed code to SproutCore now, which is outstanding for such a young project.

Now that I’m back from my trip, though, I thought we should spend a little time talking about where we are headed next.

Put simply, our next major milestone is SproutCore 1.0.  When I started planning SproutCore 1.0, here were the criteria I laid out for it:

  • Make the common easy and the uncommon possible. Typical behavior for an application should be nearly automatic without limiting a developer’s ability to hack something cool.
  • Support the whole application. SproutCore must support the whole application development process, including the model, view, and controller layers as well as design, testing, documentation, and deployment concerns.
  • A small consistent API. Favor configuration over class-bloat.  Use consistent “guessable” design patterns.  The API should be vetted well enough that it will not need to change dramatically once released.
  • Offer broad platform support. Perform well on all modern browsers.  Perform adequately on IE7 and earlier.

So far, we’ve come pretty close to achieving most of these goals.  In particular, SproutCore is the only open source web application framework that can help you build your model layer and your view layer, talk to your web server, and then tie it all together with bindings.  It is also one of the few that include documentation and unit testing right in the build tools.  The API is also pretty good when you consider how much functionality you can deliver with a few lines of code, thanks to bindings.

After watching nearly a dozen apps developed with SproutCore, though, we’ve definitely found some places we could improve both the API and the implementation.  To that end, we’ve undertaken a major review of the entire SproutCore framework over the last few weeks.  Based on that review, we are rewriting some of the oldest parts of our code to make them faster and more memory efficient and revisiting parts of our API to make them more consistent.

A lot of this work is taking place in different repositories, some of them private, so the code will not drop into the public repository until we have everything integrated.  In the mean time, I can tell you about some of the changes we have made.  Over the next few weeks, I will delve deeper into some of the specifics of our APIs, but here are some of the things we are doing at a high level:

Faster Observers and Bindings

Property observing and bindings underpin almost everything you do in the SproutCore framework.  Because of that it is really important to make this feature small and fast.  We have currently rewritten this code to make it almost 2x faster on its own, and to use significantly less memory.  More on this in the coming days.

DOM Library Independence

Currently, SproutCore depends on Prototype for a few cross-platform functions.  This really doesn’t make much sense.  In particular we think of Prototype, jQuery, and others as “DOM manipulation libraries”; somewhat like low-level drawing APIs.  SproutCore should live above this layer, allowing you to choose whichever drawing library you like to create custom views.  Additionally, removing this dependence will allow those who do not want to use Prototype to eliminate that page weight from their apps.

New Model Layer

The current implementation for SC.Store, SC.Collection, SC.Record and the servers have not been revisited since they were written almost two years ago. When these were first deployed, they worked fairly well for the small apps that used them.  Since then we’ve seen applications loading 40,000+ records into memory in a regular basis and a move towards investigating use of the coming local storage facilities on modern browsers.  This code is going to see a wholesale rewrite as we update the API to accommodate this new, larger scale world.

Better Documentation

During our revisiting of the API, we are also adding to and improving the inline documentation for all classes.  Combined with some new tutorials, the final SproutCore should be much easier to learn and to use.

And a whole lot more…

We have some other big changes in the works as well that we can’t talk about yet, but there is one more thing:

Compatibility

Even though SproutCore’s API will change, dramatically in some places, we know there is a lot of code already running on the current SproutCore API.  To support these older applications we are developing in tandem with the changes a “sproutcore-deprecated” framework.  This framework can be installed in your apps to support all of the existing API until you have a chance to update your code at your own pace.

We believe that the web is about to undergo a major revolution in terms of how we develop software.  As more applications move to the cloud, users will want to take the rich, desktop experience they are accustomed to with them.  We think that SproutCore can be one of the best options to deliver this kind of experience with our open source heritage, simple API, and focus on the whole application.  Our hope is that the new SproutCore 1.0 will solidify this case.

SproutCore Meetup at Google

The Silicon Valley SproutCore meetup group will be meeting at Google on November 12.  We are co-hosting this meeting with our friends at the Google Technology User’s Group.  Thanks especially to Van Riper who helped set this up.

The topic, of course, will be using SproutCore with Google AppEngine and Chrome.  If you are in the area, RSVP and come on by!

Meetup Page

Caching Computed Properties

David brings up another good point in a comment on my recent post about computed properties.  If you have a property that is computed but only changes occasionally, you can use a hybrid approach with both observers and computed properties to cache this value efficiently.  Here is how the fullName example we used yesterday would be implemented:

Addressbook.Contact = SC.Record.extend({

  // firstName, lastName provided automatically by server
  // computed property returns fullName.  Use cached value if
  // provided or compute.  Save computed value in cache.
  fullName: function() {
    if (!this._fullName) {
      this._fullName = this.getEach('firstName','lastName').compact().join(' ');
   }
   return this._fullName;
  }.property(),

  // clear fullName cache whenever firstName or lastName changes.
  fullNameShouldChange: function() {
    this.propertyWillChange('fullName') ;
    this._fullName = null; // clear
    this.propertyDidChange('fullName');
  }.observes('firstName','lastName')
});

Note how the computed property here is not listed as having any dependent keys.  Instead, we write both an observer and a computed property.  The observer will notify that the fullName property has changed, but avoids the cost of calculating the new fullName property itself.  This still only happens when another object tries to get() the value of the fullName property.

For an example this simple, the added overhead of managing this caching probably does not yield enough benefits, but if computing the fullName property is significantly more expensive, its easy to see how this might be useful.  Let’s see, for example, how you might use this to automatically lazily load some data.  The example below assumes you are writing a twitter client and lazily loads the twitter feed for the current user.  Whenever the user changes, it clears the feed cache so that the next time you ask for the feed it can load:

Twitter.feedController = SC.Object.create({
  // this is the name of the user you want to load
  user: 'okito',

  // computed property lazily loads the twitter feed for the
  // current user.
  feedContent: function() {
    if (!this._feedContent) this.loadFeedContentFor(this.get('user'));
    return this._feedContent;
  }.property(),

  // uses Ajax to load the new feed data, then calls
  // feedContentDidLoad() with the new feed content.
  loadFeedContentFor: function(user) {
    // loading code goes here...
  },

  // observe the user and reset the feed when needed.
  userDidChange: function() {
    this.propertyWillChange('feedContent');
    this._feedContent = null;
    this.propertyDidChange('feedContent');
  }.observes('user'),

  // called when loadFeedContentFor() gets the data.
  feedContentDidLoad: function(feedContent) {
    this.propertyWillChange('feedContent');
    this._feedContent = feedContent ;
    this.propertyDidChange('feedContent') ;
  }
});

Now an indecisive user can change their mind as often as they want about whose feed they want to view and your client won’t actually try to load the feed until you need to actually display the data.  Best of all, the rest of your application never has to understand any of this logic.  It just binds to the user and feedContent properties and trust the controller to manage it accordingly.

One More Thing… (Sorry, couldn’t resist)

You can use the technique described above to cache computed properties in your applications today.  Over the last year, though, this has become such a common design pattern, that we’ve decided to support it natively in SproutCore.  Starting with the RC1 release of SproutCore 1.0, you will be able to add cacheable() to your computed property and SproutCore will cache it for you.  Here is how you would cache the fullName example above:

fullName: function() {
  return this.getEach('firstName','lastName').compact().join(' ');
}.property('firstName', 'lastName').cacheable()

That’s it.  No observers or private variables are required.  SproutCore will automatically cache the return value of this property the first time you get() it.  It will then use the cached property until one of your dependent keys (’firstName’ or ‘lastName’ in this case) changes or until you call propertyDidChange('fullName') on the record.

This new feature will not only help you eliminate glue code but also can make your application quite a bit faster.  Don’t let that stop you from using cached computed properties today with the techniques describe above though.  It can’t really help you clean up your code.  Thanks for the tip, Dave.

Using Computed Properties To Lazily Load Data

Following up on yesterday’s computed properties post, here is a useful technique for lazily loading data using computed properties.  Let’s say your contacts application will show you a list of all the email you have exchanged with a particular contact.  This is a lot of detail that you generally will not want to load from the server but you need to have it available anyway.

How would we construct such a thing?

Well, a simple way to start would be to create a collection view that uses a property (called “messages”) on your Contact as its content:

<%= list_view :bind => { :content => "Addressbook.currentContact.messages" }, :content_value_key => :subject %>

This view helper would show a list view with the messages array.  But, this means that your Contact record has to include an array of all the messages.  We just said we didn’t want to load those messages immediately, but only when the user views a contact’s details.  How do we do this without having to write a lot of glue code?

Turns out computed properties come to the rescue.  Here is some sample code we could write:

Addressbook.Contact = SC.Record.extend({
  // ... other useful code
  messages: function() {
    if (this._messages) return this._messages; // use cache if present

    // otherwise, load from server
    Addressbook.server.loadMessagesForContact(this);
    return [];
  }.property(),

  // this method is called by Addressbook.server.loadMessagesForContact() when
  // the messages have loaded.  The passed value is an array of
  // Addressbook.Message records.
  messagesDidLoad: function(messages) {
    this.propertyWillChange('messages') ;
    this._messages = messages;  // save in cache
    this.propertyDidChange('messages') ;
  }
});

This code assumes that you also have an object called Addressbook.server with a method loadMessagesForContact() which will, as the name says, load the messages for the named contact and then calls the messagesDidLoad() method on the contact with the array of messages.

There are two really important things to notice about this code:

First, the computed property simply returns a cached value or, if it does not have the cached value, requests the value from the server.  When you first select a contact, your list view will initially display an empty list while it kicks off the request for more data in the back end.

Second, the messagesDidLoad() method, which is called when the values have loaded from the server, simply saves these in the cache and then tells SproutCore that the value of the messages property has changed.  If you are still viewing this contact’s details when this happens, this change notification will trigger the binding to your ListView and cause it to request the value of messages again, now with the loaded data.

Later, if you view other contacts and come back to this one, the messages will still be cached and will display immediately without refreshing the server.  Simple, lazily loaded data and, best of all, no glue code.

What’s This propertyWillChange/properyDidChange Thing?

The two methods called in messagesDidLoad() here might be a new trick for you.  What do these methods do?  In fact, the code you see in messagesDidLoad() is very similar to what happens whenever you call set().  They simply tell SproutCore that the value for the named property has likely changed.  This will in turn cause SproutCore to notify any observers and trigger bindings.

Usually you do not need to call these two methods yourself, but, as you can see here, they have their place.  You can use this technique anytime you have a “virtual” property like this one.   Note that the messages property above does not explicitly list any dependent property keys.  This is because the property depends on the calls to propertyWillChange()/propertyDidChange() in messagesDidLoad() to tell SproutCore when the property has changed instead.

And What If My Data Changes?

Finally, let me show you a common variant on this technique that can make it even more useful.  The above technique is useful if the data you are loading lazily will never change once you have loaded it once.   But what if this is not the case?  Maybe a new message has been received since the last time you viewed a contact.  How can you make sure your data always stays up to date?

It turns out this is quite easy.  Just rewrite your messages computed property like so:

messages: function() {
  // always refresh
  Addressbook.server.loadMessagesForContact(this);

  // then return the current value, if there is one
  return this._messages || [] ;
}.property()

Now every time some other object asks for the value of messages, it will check the server for any changes.  Of course you could rate limit this if you want to implement any other policy to control how often you check for new messages, but the principle is the same.

Computed properties are a powerful way to lazily compute - and load - data in your application.  When used with bindings, they make it easy for you to keep the data you load at one time to a minimum.  Hopefully this little demonstration will makes it a little easier to do that.

About Computed Properties

A lot of people who are new to SproutCore wonder about computed properties at some point.  What is the difference between a computed property and an observer?  This article aims to answer that question for you.  Let’s look at a common example.

Let’s say you are building a contact application, not unlike the sample you can get off of our github site.  Your Contact record would probably have the following properties.

  • firstName - the first name, comes from the server
  • lastName - the last name, comes from the server
  • fullName - the first and last name combined.  Used for display in the contact list, etc.

Obviously, you plan to get the firstName and lastName from your server, but where will the fullName come from?  After all, that is the property you want to display in your contact list.

The Observer Way

It would be best to automatically generate the fullName from your firstName and lastName.  Whenever either one of those properties changes, the fullName property should change also.  Sounds like a job for an observer function.  Your first take on this might look something like:

Addressbook.Contact = SC.Record.extend({
  // firstName, lastName both added automatically from server or fixture data
  fullName: '', // default value

  rebuildFullName: function() {
    this.setIfChanged('fullName',
      this.getEach('firstName', 'lastName').compact().join(' '));
  }.observes('firstName', 'lastName')
});

Not bad.  Nice and compact.  Now anytime firstName or lastName changes, fullName will be regenerated.  We even use the nice compact() and join() methods so that if either property is null, the name will still look nice.

But, there is a problem here.  Every time you edit firstName or lastName, you now bear the added expense of recalculating fullName.  Not such a big deal with one contact, but if you load thousands of them or even if you just want to run on Internet Explorer, this added cost really adds up.

Wouldn’t it be better if we could somehow calculate the fullName property on demand instead?  This way you could change firstName and lastName anytime you want.  You only bear the cost of recalculating the fullName property when some part of your application asks for it.  This is where computed properties come in.

The Computed Property Way

Let’s rewrite our class with a computed property now:

Addressbook.Contact = SC.Record.extend({
  // firstName, lastName both added automatically from server or fixture data

  fullName: function() {
    return this.getEach('firstName','lastName').compact().join(' ');
  }.property('firstName','lastName')
});

This code looks really similar to the example we had above, but uses some new notation you may not have seen before.  fullName now points to a function, but that function has a special “property” declaration at the end.  This is how you tell SproutCore that this method is a computed property.  Now, whenever you do something like:

contact.get('fullName');

Instead of simply returning the value of the fullName property, SproutCore will execute the function and return its result.  Hence, a computed property.

The two property names passed to the property() function tell SproutCore that the fullName computed property “depends” on both the firstName and lastName properties.  Whenever either of these properties are changed, SproutCore will also notify observers of the fullName property that it has changed also, just like what happens when you call this.set(’fullName’) in the observer example above.

This computed property approach implements basically the same behavior as we get with the observer method above, but now you only bear the cost of computing fullName when some other part of your app actually needs to use it.

Let’s say you load your contact application with 100 contacts.  Although the firstName and lastName properties will be set on all of the contacts, the fullName property will be calculated only for the 10-15 contacts or so that are visible on the screen.  Instant performance gain.

When to use Computed Properties

Computed properties are a nice way to deal with generating complex values at a lower cost, but when should you use them?

The general guideline is that you should use a computed property when you have one property that is somewhat expensive to generate and that will only be needed some of the time.  Usually you will use computed properties on model objects to compute complex properties, like above, and to lazily load data.

Once you get to the controller or view layer, computed properties have less value because a property you generate here is likely to be used all the time anyway.  Nevertheless, computed properties are another useful tool to add to your arsenal that can give your app a major speed boost, brought to you by SproutCore.

Mike Subelsky on SproutCore

Mike Subelsky, one of the people behind SproutCore-based OtherInbox, gave a great talk at B’more on Rails last week and some folks were kind enough to film it for us. It’s a big video file but a good in-depth look at how Mike is using SproutCore. Great work Mike!

Screenshot

View the movie

Thanks to Smarticus for the link!

SproutCore relaxes with CouchDB

Thanks to the hard work of Geoffrey Donaldson, SproutCore has its first major 3rd-party framework: an SC.Server subclass to interact with a CouchDB backend!

There’s a page on the SproutCore wiki with usage information and you can install the framework by doing this (note: this command should be run from the root of your sproutcore directory, the one with sc-config in it):

sc-install geoffreyd-couchdb

Then edit sc-config to load the framework into your clients:

c[:required] = [:sproutcore, :'geoffreyd-couchdb']

And restart sc-server. Head on over to the wiki to find out how to use it, and be sure to give Geoffrey feedback on the mailing list.

If you have patches, please submit them through SproutCore’s ticket system.