Offline Mode




Web Applications for Offline Use





@pleckey | http://pleckey.me

Who is this guy?


Patrick Leckey

Director of Engineering
In-Touch Insight Systems Inc.



http://www.intouchinsight.com







Online is easy

Offline is ... easy?








But what if ...









... you're not guaranteed to have connectivity the whole way ...

What we have now ...


LocalStorage

window.localStorage


ApplicationCache

window.applicationCache


Chrome: 4+, Firefox: 3.5+, Safari: 4+

Mobile Safari: 3.2+, Android Browser: 2.1+

IE: 10+


LocalStorage

simple
straightforward
rather limited


    ApplicationCache

    complex
    confusing
    powerful

      LocalStorage

      ... it's basically a shelf ...


      Put stuff on the shelf

      Take stuff off the shelf

      Can only hold so much stuff

      LocalStorage



      Similar to Cookies

      ... but not transmitted on each request



      Send what you want, when you want!

      LocalStorage



      • Key / Value pairs
      • Limited storage size




      Same Origin Policy!
      http://pleckey.me != https://www.pleckey.me

      LocalStorage



      • 5 MB limit
      • As JavaScript strings ...



      1.2345678

      4 byte float?  9 bytes of characters?

      Nope!

      18-byte UTF-16 string

      LocalStorage



      window.localStorage.getItem( 'foo' ); // null
      
      window.localStorage.setItem( 'foo', 'bar' );
      
      window.localStorage.getItem( 'foo' );  // "bar"
      
      window.localStorage.length; // 1
      
      JSON.stringify( window.localStorage ); // {"foo": "bar"}
      
      window.localStorage.removeItem( 'foo' ); // does nothing if key doesn't exist
      
      window.localStorage.clear(); // remove ALL items

      LocalStorage

      What happens when the shelf is full?



      try {
          window.localStorage.setItem( 'foo', really_big_thing );
      } catch ( e /* DOMException */ ) { 
          if ( e.name == 'QuotaExceededError' )
              // Chrome, Safari, IE
          else if ( e.name == 'NS_ERROR_DOM_QUOTA_REACHED' )
              // Firefox
      } 

      LocalStorage



      Good Ideas
      application state
      user information



        Bad Ideas
        base64-encoded images
        postal code lookup database

        ApplicationCache

        ... is basically a ... uh ... cache ...



        Pre-cache resources


        Access resources offline


        Fallback to alternates for dynamic resources

        ApplicationCache




        • No storage limit
        • Event-driven API




        Cache is based on manifest URL

        ApplicationCache





         <html manifest="/cache.manifest">




        mime type must be text/cache-manifest

          cache.manifest




          CACHE

          NETWORK

          FALLBACK

          cache.manifest

          CACHE

          CACHE MANIFEST
          # Version: 8cf54be2
          
          CACHE:
          /favicon.png
          /logo.png
          /site/page2.html



          explicitly cached resources

          cache.manifest

          NETWORK

          CACHE MANIFEST
          # Version: 8cf54be2
          
          CACHE:
          /favicon.png
          ...
          
          NETWORK:*


          resources that require the user to be online
          usually just "*"

          cache.manifest

          FALLBACK

          CACHE MANIFEST
          # Version: 8cf54be2
          
          CACHE:
          /favicon.png
          /static.html
          
          NETWORK:
          *
          
          FALLBACK:
          /dynamic.php /static.html


          resource to load if requested resource not available offline

          ApplicationCache




          cached items are always served from the application cache



          the application cache will not update unless the cache manifest file content changes

          ApplicationCache



          CACHE MANIFEST
          # Version: 8cf54be2
          
          CACHE:
          /script.js
          
          NETWORK:
          *


          text content has to be changed



          Pretty Simple, right?



          ... and you thought it'd be that easy ...

          Oops #1


          Non-cached resources don't exist.

          Even when you're online.




          NETWORK:
          * 

          Oops #2


          The application cache works with the browser cache.

          Sort of.





          Cache-Control: max-age=9999999999

          Oops #3


          Caching the cache manifest.

          It'll never update.





          Cache-Control: no-cache, no-store



          Clearing the ApplicationCache




          HTTP/1.0 410 Gone

          ApplicationCache Events


          window.applicationCache.addEventListener( ... );/**
           * 'checking' - browser is checking for an update (always first)
           *
           * 'cached' - fired after first download of a new cache manifest
           *
           * 'downloading' - new / updated resources found, browser is fetching
           *
           * 'error' - manifest returned 404 or a download failed
           *
           * 'noupdate' - cache manifest has not changed
           *
           * 'obsolete' - manifest returned 410, cache deleted
           *
           * 'progress' - X of Y manifest resources downloaded
           *
           * 'updateready' - all updates downloaded
           */


          ApplicationCache API




          window.applicationCache.addEventListener( 'updateready', function( e ) {
              alert( 'New version downloaded.  Application will now reload.' );
              window.location.reload();
          } );
          
          window.applicationCache.update();


          ApplicationCache API




          window.applicationCache.addEventListener( 'updateready', function( e ) {
              alert( 'New version downloaded.  Application will now reload.' );
              window.applicationCache.swapCache();
          } );
          
          window.applicationCache.update();


          iframe is your friend



          <!-- iframe_js.html -->
          
          <html>
          <script type="application/javascript">
          var awesome_data = { con: "foo", bar: "baz" };
          
          if ( window.parent != window ) window.parent.awesome_data = awesome_data;</script>
          </html>


          store large lookup data in iframes

          iframe is your friend


          <!-- index.html -->
          
          <html manifest="/cache.manifest">
          <iframe src="iframe_js.html" width="0" height="0" border ="0"/>
          </html>
          CACHE MANIFEST
          # Version: 1
          
          CACHE:
          /iframe_js.html
          
          NETWORK:
          *

          include lookup data in cache manifest
          load via iframe (same origin)

          iframe is your friend



          // index.html console > window.awesome_data
          Object {con: "foo", bar: "baz"}


          var appCache = window.applicationCache;
          if ( appCache.status != appCache.status.CACHED ) {
              // not cached yet, do an AJAX lookup
          } else {
              return window.awesome_data.con; // "foo"
          }

          Single Page Applications







          pre-cache static resources (speed!)

          Native(ish) Web App


          <meta name="apple-mobile-web-app-capable" content="yes"/>
          
          <meta name="mobile-web-app-capable" content="yes"/>



          runs full screen
          built-in software updates
          queue data for submission later

          What Else Can I Do With This?





          Come work for us!



          Software Developer
          User Experience
          Data Scientist



          Thank you!


          Questions?

          @pleckey | http://pleckey.com

          Offline mode

          By Patrick Leckey