Advance Offline Design and Implementation

Offline Interaction Design Reconsidered

After implementing and deploying several offline apps, I discovered that many user were confused by the interaction design proposed in the previous lecture, Design for Offline. User were confused by the meaning of the “save” and “upload” buttons. Users would ask, “When do I save and when do I upload the data?” Although the users were aware when they might be offline, they did understand understand that the “save” button would save the data to the device and that “upload” would submit the data to server. Users understand that a website saves data on the sever. They are not aware that HTML5 allows websites to save data on their device. Consequently, they do not understand the need two different saves.

Zack Duford, a former student, proposed new offline interaction. Zack’s interaction design eliminates the “save” button. Weather online or offline, the button for saving the data is “upload” (or “submit”). If the app is online then the app automatically saves the data to the server. If the app is offline the app saves the data on the device and displaces the data in a “queue”. While offline, the user can view “queue” which shows all the data ready for offline. Consequently, users can view the queue receive feedback that the data “will be uploaded”. Zack proposed that user should not be permitted to edit the data in the queue. At the time, Zack and I thought that HTML5 Service Workers would be able to detect online status and automatically upload the data in the queue to the server.

Zack made a prototype of a fishing app using Adobe XD. Fishermen can record catches that make out on lake on offline entering the location, description and photo of the catch. You can view screen shoots of Zack’s prototype in Zack’s Offline Fishing App Views. During the usability test, Zack tried to ascertain the participants mental model of the app by asking two questions after each test scenario:

  • Is this a satisfactory interface for the interaction?
  • Do you think you are being provided sufficient information about the data at any given point in time in the scenario?

Zack tested 7 participants, 3 CS majors and 4 non-CS majors. Only one non-CS major participant show some confusion by the questions. You can read Zack’s Offline Design Usability Report and Zack’s Usability Test Plan for Offline Fishing App.

Later, I discovered that the Service Workers cannot automatically upload the app’s data when an internet connection is established. After an internet connection is established, the user must interact with the app and click a “submit” or “upload” button. Nevertheless this does not invalidate Zack’s design. The queue does give the user concrete and visual display of where the data is and that it ready for uploading.

Single Page Application and Service Workers

In my previous lecture about Offline Implementation, my students and I describe an architecture for the offline component of the app that mirrors the online design pattern, meaning Model-View-Controller design pattern. The offline line component of the app had separate views that mirrored the online views and JavaScript files that assumed the role of controllers of respective controller. Finally, the were JavaScript classes that assumed the role of domain objects in online component. I proposed this architectural design because I felt this would allow developers to first develop the online app using well established frameworks, e.g. Grails. But the architectural design was awkward to maintain and inefficient. Both the offline and online views were very similar and change in one view would frequently require change the corresponding view. Also changing views would require retrieving the new view from cache.

Recently, industry proposes an new architectural for offline app, Single Page Application (SPA).  In a SPA, there is only one html or gsp file. The JavaScript code of the app control or simulate different views by hiding and showing divs. Developer has only a single code base to manage. JavaScript runs very fast and render the page quickly.

A new HTML5 standard, Service Worker, will make App Cache obsolete. The very humorous blog, Application Cache is a Douchebag, illustrates the many problems with app cache. Service workers are more flexible, but they are still not implemented in the iOS and Edge. Although the app cache is limited for our applications it suffices, but implementing offline caching using service workers is not difficult for our applications once the initial configuration is set up.

Jake Mager, a former student, developed a tutorial demonstrating implementing an offline SPA for the fishing app. To learn from Jake’s tutorial clone the repository, fishing-tutorial, onto your development machine. Import the source code into IntelliJ. Be sure that the SDK used by IntelliJ is Java 8. In the repository you find a Tutorial directory with tutorial.html. Right click tutorial.html and select Open in Browser. Follow along with the tutorial developing the app in another project.

Jake uses only JQuery show and hide functions to implement the view changes in the SPA:

$("#showCatchesButton").hide()
$("#editCatchButton").show();

The views are primarily generated by the index.js file.

Grails domain object are still used as Object Relational Model (ORM) to interface between the business code on the server and the database. Controller methods represent endpoints for AJAX calls made by JavaScript code.

The tutorial also explains how to implement service workers in Grails for caching the app. In particular the grails resource pattern must be configured:

grails:
    resources:
            pattern: '/**'

The service worker file in src/main/webapp/sw.js is simple:

self.addEventListener('install', function (event) {
    self.skipWaiting();
    event.waitUntil(
        caches.open('fishingApp').then(function (cache) {
            return cache.addAll(
                [
                    '/',
                    '/assets/jquery-3.1.1.js?compile=false',
                    '/assets/index.css?compile=false',
                    '/assets/main.css?compile=false',
                    '/assets/bootstrap.css?compile=false',
                    '/assets/bootstrap-theme.css?compile=false',
                    '/assets/bootstrap.js?compile=false',
                    '/assets/index.js?compile=false',
                    '/assets/application.js?compile=false',
                    '/assets/logo.png',
                    '/assets/localforage/localforage.js?compile=false',
                    '/assets/localforage/localforage.min.js?compile=false',
                    '/assets/localforage/localforage.nopromises.js?compile=false',
                    '/assets/localforage/localforage.nopromises.min.js?compile=false',
                ]
            );
        })
    );
    console.log('Install event:', event);
});

The service worker is registered after the document has loaded in index.js

$(document).ready(function() {

    // check if user is online with navigator
    if (navigator.onLine) {

        // check if a service worker is supported then register it
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker
                .register('/sw.js')
                .then(function() { console.log("Service Worker Registered"); });
        }
...
}