Sencha Con 2013: Fastbook

I didn’t plan on writing a post purely on Fastbook, but Jacky’s presentation just now was so good I felt it needed one. If you haven’t seen Fastbook yet, it is Sencha’s answer to the (over reported) comments by Zuckerburg that using HTML5 for Facebook’s mobile app was a mistake.

Jacky on stage

After those comments there was a lot of debate around whether HTML5 is ready for the big time. Plenty of opinions were thrown around, but not all based on evidence. Jacky was curious about why Facebook’s old app was so slow, and wondered if he could use the same technologies to achieve a much better result. To say he was successful would be a spectacular understatement – Fastbook absolutely flies.

Performance can be hard to describe in words, so Sencha released this video that demonstrates the HTML5 Fastbook app against the new native Facebook apps. As you can see, not only is the HTML5 version at least as fast and fluid as the native versions, in several cases it’s actually significantly better (especially on Android).

Challenges

The biggest challenge here is dynamically loading and scrolling large quantities of data while presenting a 60fps experience to the user. 60fps means you have just 16.7ms per frame to do everything, which is a hugely tall order on a CPU and memory constrained mobile device.

The way to achieve this is to treat the page as an app rather than a traditional web page. This means we need to be a lot more proactive in managing how and when things are rendered – something that traditionally has been in the domain of the browser’s own rendering and layout engines. Thankfully, the framework will do all of this for you.

As an example, Jacky loaded up Gmail’s web app and showed what happens when you scroll a long way down your inbox. The more you scroll, the more divs are added to the document (one new div per message). Each div contains a bunch of child elements too, so we’re adding maybe a dozen or so nodes to our DOM tree per message.

The problem with this is that as the DOM tree gets larger and larger, everything slows down. You could see the inspector showing slower and slower layout recalculations, making the app sluggish.

The solution is to recycle DOM nodes once they’re no longer visible. In this way, a list that seems to have infinite content could contain only say 10 elements – just enough to fill the screen. Once you scroll down the list, DOM nodes that scrolled off the top are detached, updated with new data and placed at the bottom of the list. Simple. Ingenius. Beautiful.

Prioritization

There’s usually a lot more going on in an app than just animating a scrolling view though. There’s data to load via AJAX, images to load, compositing, processing, and whatever else your app needs to do. And then there are touch events, which need to feel perfectly responsive at all times, even while all of this is going on.

To make this sane and manageable, we have a new class called AnimationQueue. All of the jobs I just mentioned above – handling touch events, animation, network requests and so on – are dispatched through the AnimationQueue with a given priority. Touch event handling has the top priority, followed by animation, followed by everything else.

AnimationQueue does as much as it can in that 16.7ms window, then breaks execution again to allow the browser to reflow/repaint/whatever else it needs to do. What this means is that while scrolling down a large list, it’s likely that our CPU/GPU is being taxed so much that we don’t have any time to load images or other low priority jobs.

This is a Good Thing, because if we’re scrolling through a large list there’s a good chance we are going to skip right over those images anyway. In the end they’re loaded as soon as the AnimationQueue has some spare time, which is normally when your scrolling of the list has slowed down or stopped.

Sandboxing

The final, and most complex technique Jacky discussed was Sandboxing. The larger your application gets, the larger the DOM tree. Even if you are using best practices, there’s an expense to simply having so many components on the same page. The bottleneck here is in the browser itself – looks like we need another hack.

To get around this, we can dynamically create iframes that contain parts of our DOM tree. This way our main page DOM tree can remain small but we can still have a huge application. This not only speeds up browser repaint and reflow, it also improves compositing performance, DOM querying and more.

This all happens under the covers and Jacky’s aiming on including Ext.Sandbox in Sencha Touch 2.3 so that all apps can take advantage of this huge improvement. He cautioned (rightly) that it’ll only make 2.3 if it’s up to his high standards though, so watch this space.

Offline Apps with HTML5: A case study in Solitaire

One of my contributions to the newly-launched Sencha Touch mobile framework is the Touch Solitaire game. This is not the first time I have ventured into the dizzying excitement of Solitaire game development; you may remember the wonderful Ext JS Solitaire from 18 months ago. I’m sure you’ll agree that the new version is a small improvement.

Solitaire is a nice example of a fun application that can be written with Sencha Touch. It makes use of the provided Draggables and Droppables, CSS-based animations, the layout manager and the brand new data package. The great thing about a game like this though is that it can be run entirely offline. Obviously this is simple with a native application, but what about a web app? Our goal is not just having the game able to run offline, but to save your game state locally too.

The answer comes in two parts:

Web Storage and the Sencha data package

HTML5 provides a brand new API called Web Storage for storing data locally. You can read all about it on my Web Storage post on Sencha’s blog but the summary is that you can store string data locally in the browser and retrieve it later, even if the browser or the user’s computer had been restarted in the meantime.

The crucial part of the sentence above is that we can only store string data. In the case of a game of Solitaire we need to store data on the elapsed time and number of moves as well as the location and status of each card. This doesn’t sound like the kind of data we want to manually encode into a string, so thankfully the data package comes to the rescue.

The Sencha Touch data package is a complete rewrite of the package that has been so successful in powering Ext JS 3.x. It shares many of the same philosophies and adds the learning we have gained from developing Ext JS 3.x over the past year. One of the new capabilities it offers us is a Local Storage proxy, which automatically marshalls your model data into local storage and transparently restores it when you need it.

Using the new proxy is simple – all we need to do is set up a new Store, specifying the Proxy and the Model that will be saved to it. Models are the spiritual successor to Ext JS 3.x’s Records. Now whenever we add, remove or update model instances in the store they are automatically saved to localStorage for us. Loading the store again is equally easy:

//set the store up
var gameStore = new Ext.data.Store({
    proxy: new Ext.data.LocalStorageProxy({
        id: 'solitaire-games'
    }),
    model: 'Game'
});

//saves all outstanding modifications, deletions or creations to localStorage
gameStore.sync();

//load our saved games
gameStore.read({
    scope: this,
    callback: function(records) {
        //code to load the first record
    }
});

And just like that we can save and restore games with Web Storage. We can visit our app’s webpage and start a game then come back later and find it automatically restored. But we still can’t play offline, for that we need the application cache.

The HTML5 Application Cache Manifest

The application cache is one of the best features of HTML5. It provides a simple (though sometimes frustrating) way of telling the browser about all of the files your application relies on so that it can download them all ready for offline use. All you have to do is create what’s known as a manifest file which lists all of the files the application needs – the Solitaire manifest looks like this:

CACHE MANIFEST
#rev49

resources/icon.png
resources/loading.png

resources/themes/wood/board.jpg
resources/themes/wood/cards.png

resources/css/ext-touch.css
resources/solitaire-notheme.css
resources/themes/wood/wood.css
resources/themes/metal/metal.css

ext-touch-debug.js
solitaire-all-debug.js

We tell the browser about the manifest file by pointing to it in the tag’s manifest atttibute. When the browser finds this file it downloads each of the listed assets so that they are ready for offline consumption. Note that it does not automatically include them on the page, you still need to do that yourself via the usual link and script tags. Here’s a snippet of the Solitaire index.html file:

<!doctype html>
<html manifest="solitaire.manifest">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">	
        <title>Solitaire</title>

        <link rel="stylesheet" href="resources/css/ext-touch.css" type="text/css">
        <link rel="stylesheet" href="resources/solitaire-notheme.css" type="text/css">
        <link rel="stylesheet" href="resources/themes/wood/wood.css" type="text/css">

        <script type="text/javascript" src="ext-touch-debug.js"></script>
        <script type="text/javascript" src="solitaire-all-debug.js"></script>

Note the manifest file definition in the html element at the top, and the fact that we still include our page resources the normal way. It sounds easy, but without a little setup first it can be a very frustrating experience. Usually your browser will try to cache as many files as possible, including the manifest file itself – we don’t want this. As soon as your browser has a long-term cache of the manifest file it is extremely difficult to update your application – all of the files are already offline and won’t be updated, and the browser won’t even ask the server for an updated manifest file.

Preventing this behaviour turns out to be fairly easy, and the solution in its simplest form comes in the shape of a .htaccess file with contents like the following:

<Files solitaire.manifest> 
    ExpiresActive On 
    ExpiresDefault "access" 
</Files>

This directs Apache to tell the browser not to cache the manifest file at all, instead requesting the file from the server on every page load. Note that if the device is currently offline it will use the last manifest file it received.

This is half the battle won, but let’s say you change one of your application files and reload – you’ll find nothing happened. This is because when your browser asked the server for the manifest file it actually asked if the file had changed or not. As the manifest itself wasn’t updated, the server responds with a 304 (Not Modified) and your browser keeps the old file.

To make the browser pick up on the change to the application file you need to update the manifest file itself. This is where the mysterious “#rev49” comes in on the manifest example file above. This is a suggestion from the excellent diveintohtml5 article on the subject – whenever you change any application files just bump up the revision number in the manifest file and your browser will know to download the updated files.

One final detail is that your Apache server probably isn’t set up to server manifest files with the correct mime type, so be sure to add the following line to your Apache config and restart the server:

AddType text/cache-manifest .manifest  

Wrapping it up

Offline access is a big deal for mobile apps and Sencha Touch makes them much easier to write. The benefit is not so much that the apps can run without an internet connection (many modern touch devices have a near-permanent connection to the internet already), but that web apps can now be treated as first-class citizens alongside native apps.

The fact that many devices allow your users to save your app to their home screen and load it as though it were native is an important step – you keep all of the advantages of web app deployment while gaining some of the benefits of native apps. As more and more native hardware APIs become available to web apps their importance will only grow.

If you want to check out Solitaire’s offline support for yourself visit the application’s site and save it to your iPad’s home page. Try turning on airplane mode and loading the app and see how it behaves as though it were native. If you don’t have an iPad, you can load the app in up-to-date versions of Chrome or Safari and get a similar experience.

%d bloggers like this: