Skip to content

kafeiinteractif/offline-mode

Repository files navigation

Taking Drupal 8 Sites Offline: airplane mode surfing!

Ryan Weal

Kafei Interactive Inc.

DrupalCamp Northern Lights, Reykjavík, Iceland

Formatted as slides here: https://kafeiinteractif.github.io/offline-mode/#/

Why talk about offline?

  • Wifi not always on board aircraft
  • Travelling internationally without data
  • Bored while on the bus
  • Networks can be slow, load time is slow
  • Adventure awaits (when you are out in the mountains!)
  • Internet goes down due to IoT devices, undersea cables, etc.

Planning for Offline

  • Decide what content you need offline
  • Do you want fallback content if not online?
  • What will we sync with an online Database?

Architecture considerations...

  • How long should your initial page load take?
    • People generally abandon requests of 10s or more.
  • Keep it light! 5MB or less is ideal.
    • Storage limits set by browser. See "The Offline Cookbook" for more details.
    • You may use IndexedDB to get around this*
  • Will you need a companion desktop application? (ie, Electron?)

Does this sound familiar?

  • Content Security Policy (CSP)
    • Forces same origin - no CDN, embeds, AJAX*
  • Bigpipe loads the initial shell of the site
  • Cache expiry headers
  • XMLsitemap lists all resources on a site

appcache (first strategy)

  • Adds a metadata field to your HTML

  • Specifies a manifest file that lists the resources

  • Downloads ALL the resources on the first page hit

  • Content is served locally first, until new manifest

  • Just works™

  • Depreciated (this is why we can't have nice things)

  • Currently the ONLY way to do offline on Apple devices

Our first (non-Drupal) appcache site!

appcache index.html

<!doctype html>
<html manifest="bulletpad.appcache">
<head>
  <meta charset="utf-8">
  <title>Bulletpad</title>
  <script type='text/javascript' src='vue.js'></script>
  <script type='text/javascript' src='main.js'></script>
  <link rel="stylesheet" type="text/css" href="stylesheet.css">
  <meta name="viewport" content="width=device-width">
</head>
<body>

<div id="app">
  <textarea v-model="newTodo" v-on:keyup.enter="addTodo" placeholder="new note >"></textarea>
  <ul>
    <li v-for="todo in todos | reverse">
      {{ todo.text }}
      <button v-on:click="removeTodo($index)">×</button>
    </li>
  </ul>
</div>

</body>
</html>

This uses vue.js, btw... we love it.

appcache bulletpad.appcache

CACHE MANIFEST
# v11

# assets
CACHE:
index.html
stylesheet.css
main.js
vue.js

NETWORK:
*

No offline fallback in this case, but it is possible.

(100% of this "site" is in the manifest)

appcache demo app

https://kafeiinteractif.github.io/bulletpad/ screenshot of example app

appcache debugging

  • Keep the console log open
  • Update the serial "number" in manifest with each change
  • Chrome dev tools Network tab has "disbable cache" and "offline" modes
  • New version loads after serving old verison to user

appcache basics: further reading

"A Beginner's Guide to Using the Application Cache" https://www.html5rocks.com/en/tutorials/appcache/beginner/

"Application Cache is a Douchebag" http://alistapart.com/article/application-cache-is-a-douchebag

appcache drupal module

"Offline Application"

Made for Frontend United 2016 by swentel

https://www.drupal.org/project/offline_app

offline_app assumptions

  • All the content is going to fall under /offline/
  • Presumes you will have one level of alias, no / in paths!
  • Suppresses theme from rendering
  • Three new block regions
  • Does not load images or other assets on nodes
  • Will not let you set custom homepage fallback

Very tempting to try putting paths in first page of config... don't! Use the Content and Assets tabs.

offline_app setup

  • Enable permissions for anonymous user:

    • Access Application Cache manifest
    • Access Homescreen manifest
    • Access offline content
  • Add the manifest to pages using one of:

    • default behavior adds iframe to sidebar_first; or...
    • admin/config/services/offline-app checkbox for "Add the manifest attribute to the HTML tag..."
  • Clear the cache

offline_app content type configuration

  • Use content type manage display "custom display settings" to add either (or both) the "offline page" and "offline teaser" view modes.

  • After enabling view mode go to /admin/config/services/offline-app/content

  • Input a node path: beauty/node:1

  • Now /offline/beauty should return /node/1 (rebuild cache?)

offline_app views configuration

  • Add an "offline" display to the view

  • Show: "content" with "offline page" view mode

  • Reference it in our config:

    articles/view:super_awesome:offline_1

  • Now /offline/articles will show the view called "super_awesome" using the offline_1 variant

offline_app config complete

config page screenshot for offline_app

offline_app ... much wow!

offline_app final result

offline_app potential problems!

  • Nothing added to the manifest automatically (nodes, etc)

  • Add stylesheets to your Assets list, but paths can change...

  • When (and how) to reload with new content

  • No option for separate caches for shell/content

  • Visit the homepage, check that tag has manifest reference

  • Roll your own implementation?

appcache and offline_app: further reading

"Let's make Drupal 8 available offline using appcache" https://www.agiledrop.com/blog/lets-make-drupal-8-available-offline-using-appcache

Service Worker (second strategy)

  • Also known as: "Progressive Web Apps!" (PWA)
  • Can be added to user's Android homescreen*
  • Encouraged to load the shell first, fetch content later...
  • Multiple caches! As many as you want.
  • Our demo code: https://github.com/kafeiinteractif/bulletpad_pwa

Service Worker: Characteristics

  • Manifest file is now in JSON format
  • Requires JavaScript to register the "Service Worker"
  • Recommended to do things like use a loading icon
  • Code can target different stages: install / activate / fetch
  • You are free to create many types of caches for your data
  • Intercepts requets so you can choose offline or online content

Service Worker: Is it ready?

Service Worker demo

https://kafeiinteractif.github.io/bulletpad_pwa/ screenshot of bulletpad_pwa

Service Worker pwa appears "standalone"

screenshot of bulletpad varients in application switcher

Define the caches and context

var cacheName = 'bulletpad-x6'; // app shell
var filesToCache = [
  '/',
  '/index.html',
  '/main.js',
  '/vue.js',
  '/stylesheet.css',
  '/service-worker.js',
];

Setting up our app by caching the shell

self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
      console.log('[ServiceWorker] Caching app shell');
      return cache.addAll(filesToCache);
    })
  );
});

If installed correctly this will check for a new version on first launch after reboot.

self.addEventListener('activate', function(e) {
  console.log('[ServiceWorker] Activate');
  e.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (key !== cacheName) {
          console.log('[ServiceWorker] Removing old cache', key);
          return caches.delete(key);
        }
      }));
    })
  );
});

Interceping any http request, serving local if possible!

self.addEventListener('fetch', function(e) {
  console.log('[ServiceWorker] Fetch', e.request.url);
  e.respondWith(
    caches.match(e.request).then(function(response) {
      return response || fetch(e.request);
    })
  ); // iterate on your other caches
});

Many alternate variations of this: can do cache-only, network-only, network-fallback, or race network vs. cache! Also: cache-then-network, or offline.html message... examples in "Offline Cookbook" (see last slide).

Service Worker debugging

  • Needs https or "localhost" domain to test/run
  • Update the cache key(s) with each revision
  • console_log is your friend
  • chrome://inspect/#service-workers
  • chrome://serviceworker-internals
  • Test with Google Lighthouse (new!)

Service Worker: potential problems

  • Every change can make the entire site re-download (but you can design around this)
  • Devices have storage limits, query them! Find out if full.
  • No reload button for Chrome on Android (pull down to reload)
  • Unless you use Lighthouse to test you may have bugs interfering with offline capability!

Service Worker Drupal Modules

"Progressive Web App" by nod_

for Drupal 7 and Drupal 8

https://www.drupal.org/project/pwa

"Service Worker" by nod_

No official release yet. Looks to be a demo in favor of above.

https://www.drupal.org/project/serviceworker

Service Worker PWA Drupal Module

  • has support for "installing" the site, icon config

  • choose standalone app, fullscreen mode, minimal UI

  • pick what URLs to cache on install

  • intended to help speed up CSS and JS

  • tries to be aware of images, etc. but does not cache them

  • cache key is hard to update

Service Worker new features

Google announced in February 2017 that Service Worker apps will now have all of the other capabilities of a native app, including:

  • generated APK file
  • install/uninstall functionality
  • visible in apps drawer, not just homescreen

... but you cannot submit that APK to the Play store.

Service Worker, in Drupal Core?

This idea has been floated as a demonstration of basic functionality that would be "out of the box" functional.

Target audience would be camps primarily. More advanced use cases would make their own module.

https://www.drupal.org/node/2830668

Service Worker: further reading

"Your First Progressive Web App" https://developers.google.com/web/fundamentals/getting-started/codelabs/your-first-pwapp/

"Adding a Service Worker and Offline into your Web App" https://developers.google.com/web/fundamentals/getting-started/codelabs/offline/

More Good Stuff

"The offline cookbook" https://jakearchibald.com/2014/offline-cookbook/

"Web App Install Banners" https://developers.google.com/web/fundamentals/engage-and-retain/app-install-banners/

"Electron" http://electron.atom.io/

You can also render your PWAs as desktop apps. Electron is a wrapper for Chrome's rendering engine.

Thanks!

https://github.com/kafeiinteractif/offline-mode

https://twitter.com/ryan_weal

About

Conference talk slides about creating your own progressive web app, comparing appcache and Service Worker and how they can be used in Drupal (or as standalone without!)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published