The wearther blog

engineering

Node.js + Backbone = wearther

I would like to share how wearther code is achitected. Hopefully this post will be useful for your own projects.

Wearther is built on top of Backbone for the front-end and Node.js as its backend. The backend stuff is pretty simple, just a small webserver that handles different routes, requests external API calls and the optimizer to calculate clothing combinations. I want to focus on the front-end side of things instead.

First off, I decided to separate each component to a separate module and store it in their own separate directories. So we left with the following directory structure:

modules/
  |-combinations/
  | |-collections/
  | | |-combinations.js
  | |-models/
  | | |-combination.js
  | |-views/
  | | |-combination.js
  | | |-combinations.js
  | |-Combinations.js
  |-listings/
  | |-...
  | |-Listings.js
  |-location/
  | |-...
  | |-Location.js
  |-router/
  | |-Router.js
  |-settings/
  | |-Settings.js
  |-temperature/
  | |-...
  | |-Temperature.js
Util.js
WeartherApp.js

WeartherApp.js is the main file where it listens to events and deal with them accordingly. It's also the point of entry for the entire app.

Originally each module can communicate to each other directly but I soon realized that it's not going to be pretty once I start to add more modules in the future. Eventually it will be a very tightly coupled app. So I decided to look for alternative pattern and soon discovered Mediator[1][2] pattern.

A quick note: mediator pattern allows the app to be loosely coupled by not a letting modules to communicate to each other directly. This is achieved by passing the mediator object around. Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var mediator = {
  subscribe: function(channel, fn) {
    // store the function into the channel
  },
  publish: function(channel) {
    // notify the channel
  }
};

function Module1(mediator) {
  function doSomething() {
    // do stuff

    // let mediator knows we're finished
    mediator.publish('module1:done');
  }
}

function Module2(mediator) {
  mediator.subscribe('module1:done', function() {
    // module 1 is done, do something here
  });
}

I like mediator pattern approach but I'm still not entirely satisfied. I don't like the idea of passing the mediator object around (in this case WeartherApp) to each module. I don't like the extra parameter in my function constructors so I came up with mediator-like convention: a slightly modified Event Aggregator pattern (note that I mentioned convention, because it is very easy to mess things up if I'm not following my own convention).

The idea is very simple. Each module can broadcast and listen to message, but they are not allowed to directly communicate with other module. Every communication is handled by the main object (WeartherApp). To achieve this, all modules inherited Backbone.Event class so it can call trigger method.

WeartherApp then load all the modules and listens to the broadcast messages and react accordingly

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
*Location.js*
function getLocation() {
  // obtain location
  var location = readGeolocation();
  this.trigger('location:locationUpdated', location);
}

*Temperature.js*
function getTemperature(location) {
  // do ajax call to server
  var temp = $.ajax(...)
  this.trigger('temperature:temperature', temp);
}

*Combination.js*
function calculateCombinations(temp) {
  var combinations = $.ajax(...)
  this.trigger('combination:combination', combinations);
}

*WeartherApp.js*
locations.on('location:locationUpdated', function(newlocation) {
  temperature.getTemperature(newlocation);
});

temperature.on('temperature:temperature', function(newtemp) {
  combinations.calculateCombinations(newtemp);
});

// start the app
location.getLocation();

What I like about this convention is that I don't have to pass around the main object (mediator), instead each module simply broadcast its own message without any dependency to the main object. The obvious downside is that it is possible for each module to talk with each other. That's why by using the convention, the module should not listen to any message at all. It can only broadast and it's up to the main object to handle them.

Another reason why I like this is that the inner module implementation can be as convoluted as you wish without affecting other module. For example in Combination module, the implementation (models, collections, views) can interact with each other (model can notify collection and vice-versa) while the main module container is the one wrapping them all up and broadcast message to the main object whenever required.

I hope you find this post useful. Let me know what you think :)

- Ronald

Why mobile web application sucks

Originally I started wearther as a mobile web application - webapp out of the curiousity of the web technology. I was just started learning JavaScript and was so inspired by Forecast.io (try visiting Forecast.io using an iPhone). To average users, they wouldn't even notice that Forecast.io is not a native app!

As soon as I started working on wearther mobile web application, I noticed some serious problems and limitations (note that problems 1-4 are iOS specific problems):

  1. Bookmarklet freezes! This one is a huge issue for me, when opening bookmarklet on iOS, there are times where it just froze and simply won't respond to anything. Turning screen off doesn't work, the same goes with pressing home button (although Siri is still active) [1] [2] [3]. I started noticing this on iOS 6 and was hoping that this will be fixed on iOS 7. Unfortunately that's not the case. In fact, iOS 7 introduces severe bookmarklet problems [4] [5]!

  2. No multitasking. This irks me and Enrico the most. Often when checking the weather we got distracted by new email or tweets, so we simply switch app. Upon switching back to wearther, we have to start all over again from the beginning (obtaining location/temperature/combinations).

  3. Updating the webapp is a pain. Sometimes you have to delete the old shortcut and re-add it again (if you made a significant change on the <head> tag)

  4. Location permission expires. I haven't fully confirm this but after some time wearther will lose permission to use geolocation. The only solution was to recreate the bookmarklet. This is a big no-no.

  5. Compatibility issues. It's getting better everyday but one big issue I faced was running wearther on Samsung Galaxy S3 was border-radius style was not respected. Best part is, it's Samsung Galaxy S3-specific bug.

I admit I was very slow to decide to move on to native iOS, but it's better late than never. Switching to native iOS opens up a huge possibilities for the upcoming features. I'm really excited and looking forward to it!

- Ronald

Change on Apparent Temperature Formula

We recently took notice that wearther was not proving the right clothing combinations when temperatures were greater than 27 °C / 80 °F. We quickly realized that it was due to the formula of one of the temperature indicators that we currently use - Apparent Temperature. Because of this, wearther was providing us with combinations that involved too many layers of clothing.

As of current, wearther makes use of the Australian Apparent Temperature which takes into account wind speed. This works great but we missed out on the fact that it was meant to be used during cooler and not warmer temperatures. As a result we improved on the Apparent Temperature formula to include the Heat Index to represent warmer temperatures, while keeping the AAT for cooler temperatures.

As spring has arrived in the southern hemisphere, we believe that this change will make wearther more relevant to our friends in the northern and southern hemispheres.