Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
elmar_kuesters
Discoverer

Topic

This project demonstrates how to develop an offline capable OpenUI5 application using the new ServiceWorker API which needs no native code.

Project Sources: https://github.com/pensoffsky/OpenUI5-ServiceWorker

You can try it out in current Chrome, Opera or Android browsers: https://pensoffsky.github.io/OpenUI5-ServiceWorker/index.html

(Open the link in your browser and let the app load. This puts all the needed files into the cache of the ServiceWorker. Then close your browser, go into airplane mode and reopen the URL. The app should start normally. If it does not work then maybe your browser is not supported yet. List of supported browsers)

Disclaimer

  • this concept does unfortunately not work with "normal" UI5 resources because they use sync XMLHttpRequests which are not supported by the ServiceWorker API. The project uses modified UI5 resources created by matz3 that load async.
  • only https is supported by ServiceWorker because of security reasons
  • the ServiceWorker API is not yet supported in all browsers. You can check the availability here
  • the delayed loading of the UI5 resources was adapted from this concept: Asynchronous load of SAPUI5

Features

  • Graceful degradation
    • if the ServiceWorker API is not available in the browser than the feature is ignored and it works as a normal online application
  • Start of app
    • the first time you open the application all needed resources are loaded from the server and cached in the browser. After this the next time you open the application it is served directly from the ServiceWorker cache. This means you can disconnect your internet connection and still open the app in the browser just by navigating to the URL.
  • Querying of stock price
    • if you have an internet connection then the http request goes directly to the yahoo webservice. The result is then written to the ServiceWorker cache and displayed in the app. (the timestamp in the response changes)
    • if you are offline then the request is answered from the cache. The last successful cached request is returned.

Start of app ONLINE

Here you see that the request to yql (yahoo api) is live and 2.5KB in size. So the user gets live data when he clicks the "refresh stock price" button.

Start of app OFFLINE

The browser is set to Offline mode and the page is reloaded.

Here you see that the request to yql (yahoo api) is answered from the ServiceWorker cache after the initial request failed. So the user gets the stock price from the last time he used the application.

Files

  • index.html
    • main entry file. Installs the ServiceWorker, loads OpenUI5 resources and instantiates the app.
  • sw.js
    • contains the ServiceWorker definitions
    • the new Fetch API is used extensively
  • dist/
    • location of the OpenUI5 resources

index.html

Depending on the availability of the ServiceWorker API  the ServiceWorker is registered. Only after the registration is over then the UI5 resources are loaded.


if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('sw.js').then(function(registration) {
            // Registration was successful
            console.log('ServiceWorker registration successful with scope: ', registration.scope);
            //now load the UI5 resources
            fLoadUI5();
        }).catch(function(err) {
            // registration failed
            console.log('ServiceWorker registration failed: ', err);
            //now load the UI5 resources
            fLoadUI5();
        });
    } else {
        //if the browser does not support serviceworker then just load the app
        fLoadUI5();
    }






sw.js

First we define which files we want to cache for offline use


var urlsToCache = [
    '/',
    '/dist/resources/sap-ui-custom.js',
    '/dist/resources/sap/m/library-preload.json',
    '/dist/resources/sap/ui/core/themes/sap_hcb/library.css',
    '/dist/resources/sap/m/themes/sap_hcb/library.css',
    '/dist/resources/sap/ui/core/themes/base/fonts/SAP-icons.ttf',
    '/dist/resources/sap/ui/thirdparty/unorm.js', //needed for safari
    '/dist/resources/sap/ui/thirdparty/unormdata.js' //needed for safari
];





then we hook the install event of the ServiceWorker. This is used to add all the defined URLs to the cache.


self.addEventListener('install', function(event) {
    // Perform install steps
    event.waitUntil(
        caches.open(CACHE_NAME)
        .then(function(cache) {
            console.log('Opened cache');
            return cache.addAll(urlsToCache);
        })
    );
});





we also hook the fetch event. This event is triggered when the webApp is doing a http request. We check based on the URL what the app is requesting and what caching logic we want to use.


self.addEventListener('fetch', function(event) {
    if(event.request.url.startsWith("https://query.yahooapis.com")){
 
        ...
 
    } else {
        ...
    }
});




For the yahoo API we first try to get the data via http request and only if this does not work we answer the request from the ServiceWorker cache. When the http request was successful we add it to the cache so that is is available for the next offline start of the application.


console.log('ServiceWorker: yahoo query: detected');
        //for yahoo queries: online first  
        var fetchRequest = event.request.clone();
        event.respondWith(fetch(fetchRequest).then(
          function(response) {
            // Check if we received a valid response
            if(response && response.status === 200 ) {
                console.log('ServiceWorker: yahoo query: fetch OK');
                var responseToCache = response.clone();
                //valid webrequest, clone and cache the result
                caches.open(CACHE_NAME).then(function(cache) {
                    console.log('ServiceWorker: yahoo query: added to cache');
                    cache.put(event.request, responseToCache);
                });
                return response;
            } else {
                console.log('ServiceWorker: yahoo query: fetch FAILED');
                //webrequest FAILED, try to answer from cache
                caches.match(event.request).then(function(response) {
                    //we dont care if the response was ok or NOT
                    console.log('ServiceWorker: yahoo query: answered from cache' + response);
                    return response;
                });;
            }
          }
      ).catch(function(error){
          console.log('ServiceWorker: yahoo query: fetch EXCEPTION' + error.message);
          //webrequest FAILED, try to answer from cache
          return caches.match(event.request).then(function(response) {
              //we dont care if the response was ok or NOT
              console.log('ServiceWorker: yahoo query: answered from cache' + response);
              return response;
          });;
      }));



The static resources we always try to get from the ServiceWorker cache and only if this does not work we do a live http request.


event.respondWith(
            //check if the request can be answered from the offline cache
            caches.match(event.request).then(function(response) {
                if (response) {
                    // Cache hit - return response from cache
                    console.log('ServiceWorker: found:' + event.request.url);
                    return response;
                }
                console.log('ServiceWorker: NOT FOUND:' + event.request.url);
                return fetch(event.request);
            })
        );

Links

UI5 offline application using AppCache and LocalStorage

2 Comments