UI5 offline application using ServiceWorker API
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);
})
);
How to enable service worker for SAP Fiori App, as we know index.html file will be ignored in the fiori lounchpad, can I write the index.html file part in the component.js file?
Regards,
Azhar
Hey there,
in short: yes you can. You can simply register your service worker using "navigator.serviceWorker.register".
Though I'd imagine in an FLP context you'd have be a bit more strict/careful as to how your SW implementation looks like and/or the timings of things. In general though, it is possible.
I'd also recommend to check out this sample application and it's custom service worker implementation.
BR,
Marco