Offline Mobile Web

App Architecture & Design

Boaz Sender / @boazsender

## Boaz Sender * Work at [Bocoup](http://bocoup.com) * @boazsender on [twitter](http://twitter.com/boazsender) & [github](http://github.com/boazsender) * [boazsender.com](http://boazsender.com) on the www * boaz on [freenode](http://webchat.freenode.net/)
# Why? ### (630 Million smarthones bought in 2012) * Android (453,070,000) * iOS (126,220,000) * Blackberry (33,640,000) * Windows mobile (18,410,000)

Battery

## Radio
# Connectivity
## How do we solve these problems?

Optimized Assets

boaz@gallina:~/git/app $ grunt

Running "clean:0" (clean) task
Cleaning "dist/"...OK

Running "lint:files" (lint) task
Lint free.

Running "csslint:base_theme" (csslint) task
Lint free files: 1

Running "jst:dist/debug/templates.js" (jst) task
File 'dist/debug/templates.js' created.

Running "requirejs" task

/home/boaz/git/bcc/dist/debug/require.js
----------------
/home/boaz/git/bcc/assets/js/libs/jquery.js
/home/boaz/git/bcc/assets/js/libs/lodash.js
/home/boaz/git/bcc/assets/js/libs/backbone.js
/home/boaz/git/bcc/assets/js/plugins/backbone.layoutmanager.js
/home/boaz/git/bcc/assets/js/plugins/backbone.search.js
/home/boaz/git/bcc/assets/js/plugins/backbone.routefilter.js
/home/boaz/git/bcc/app/app.js
/home/boaz/git/bcc/app/modules/page.js
/home/boaz/git/bcc/app/modules/navigation.js
/home/boaz/git/bcc/app/modules/appstate.js
/home/boaz/git/bcc/app/modules/notifications.js
/home/boaz/git/bcc/app/modules/search.js
/home/boaz/git/bcc/app/modules/overlay.js
/home/boaz/git/bcc/app/modules/header.js
/home/boaz/git/bcc/app/modules/util.js
/home/boaz/git/bcc/app/modules/pagetypes/list.js
/home/boaz/git/bcc/app/modules/pagetypes/content.js
/home/boaz/git/bcc/app/modules/pagetypes/contentlist.js
/home/boaz/git/bcc/app/modules/pagetypes/event.js
/home/boaz/git/bcc/assets/js/libs/leaflet.js
/home/boaz/git/bcc/app/modules/pagetypes/map.js
/home/boaz/git/bcc/app/modules/pagetypes/mapconfig.js
/home/boaz/git/bcc/app/modules/pagetypes/notfound.js
/home/boaz/git/bcc/app/router.js
/home/boaz/git/bcc/assets/js/plugins/orientationchange.js
/home/boaz/git/bcc/app/main.js
/home/boaz/git/bcc/app/config.js


Running "concat:dist" (concat) task
File "dist/debug/require.js" created.

Running "stylus:compile" (stylus) task
File dist/debug/index.css created.

Running "appcache:dist" (appcache) task

Running "min:dist/release/require.js" (min) task
File "dist/release/require.js" created.
Uncompressed size: 733085 bytes.
Compressed size: 82898 bytes gzipped (269063 bytes minified).

Running "mincss:dist/release/index.css" (mincss) task
File dist/release/index.css created.
Uncompressed size: 32182 bytes.
Compressed size: 5041 bytes gzipped (19684 bytes minified).

Running "appcache:dist" (appcache) task

Done, without errors.

Single Page

template = _.template('<header><h1><%= pageObj.title %></h1></header><section><p><%= pageObj.text %></p></section>');

var pageObj = { 
  title: "jQUery Toronto",
  text: "Although many legends exist about the..."
};

template( pageObj );

Declaration

http://app.com/api/full-state


{
  "name": "jQuery Toronto",
  "lang": { "a bunch of layout text"},
  "revision": "1",
  "pages": [ pageObj, pageObj, pageObj ],
  "navigation": [ navObj, navObj, navObj ]
}
  

Page Object


{
  "name": "home",
  "route": "",
  "type": "list",
  "content": {
    "header": "jQueryTo",
    "children": [
      "sponsors",
      "about",
      "venue",
      "schedule",
      "speakers"
    ]
  }
}

Navigation Object


{
  "text": "Home",
  "route": "/",
  "icon": "iconsprite.png",
  "coordinates": [ x,y ]
}

Flat Page Schema


{
  "name": "jQuery Toronto",
  "lang": { "a bunch of layout text"},
  "revision": "1",
  "pages": [ pageObj, pageObj, pageObj ],
  "navigation": [ navObj, navObj, navObj ]
}

Not nested


{
  "name": "jQuery Toronto",
  "lang": { "a bunch of layout text"},
  "revision": "1",
  "pages": [{
      name: "foo",
      children: [ pageObj, pageObj, pageObj ],
    },
    {
      name: "bar",
      children: [ pageObj, pageObj, pageObj ],
  }]
  "navigation": [ navObj, navObj, navObj  ]
}

Traunching

http://app.com/api/initial-state


{
  "name": "jQuery Toronto",
  "lang": { "a bunch of layout text"},
  "revision": "1",
  "pages": [ {...}, {...} {...} ],
  "navigation": [ {...}, {...} {...} ]
}

http://app.com/api/pages


{
  "revision": "2",
  "pages": [ pageObj, pageObj, pageObj ]
}

Other endpoints to consider

  • full-data
  • initial-data
  • pages
  • pages?first=[:id]&last=[:id]
  • changes?since=[epochdate]
  • page/[:id]

Defalt GET Query Params

  • ?revision=[revisionnumber]
  • ×tamp=[epochdate]
## Consuming the data

Appstate


appstate = Backbone.Model.extend({
  url: "http://app.com/api/full-state",
  initialize: function() {
    // Initialize self from localstorage
    // Bind to reserialize on change
  },
  parse: function( resp ) {
    // Construct server response into a into a backbone model

  }
});

View map


var pagetypes = {
  list: Backbone.View.extend({
    template: templateFunc,
    tagName: "ul",
    serialize: function() { ... }
  }),
  content: Backbone.View.extend({ ... }),
  event: Backbone.View.extend({ ... }),
  map: Backbone.View.extend({ ... })
};

{
  "name": "home",
  "route": "",
  "type": "list",
  "content": { ... }
}

Routing


var Router = Backbone.Router.extend({
  routes: {
    "*path": "page"
  },
  page: function( route ){
    route = decodeURI(route);
    var page = appstate.get("pages").where({ route: route })[0];
    var pagetype = page.get( "type" );
    var currentView = new pagetypes[ pagetype ]({ model: page });
    currentView.render();
  }
});

Maps (heavy behavior)


{
  "name": "Map of Toronto",
  "route": "map-of-toronto",
  "type": "map",
  "content": {
    "tileUrlTmpl": "path-to-tile-server-endpoint",
    "mapcenter": [43.40, -74.24],
    "zoom": 15,
    "optionspage": "map-of-toronto-opts"
}
* Google Maps * Bing Maps * JqueryGeo * Leaflet ← the best one * Mapquest * Polymaps

manifest.appcache

(MIME type text/cache-manifest)


CACHE MANIFEST
# Wed Feb 20 2013 01:19:00 GMT-0500 (EST)

CACHE:
/index.html
/assets/js/libs/dist.js
/assets/css/index.css
/assets/img/sprite.png
/assets/img/loading.png

NETWORK:
http://app.com/api/

The most important bookmarklet

chrome://appcache-internals/

## Dont copy native
## Questions [@boazsender](http://twitter.com/boazsender) / [boazsender.com](http://boazsender.com)