<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Leon Hassan]]></title><description><![CDATA[Developer, bookworm and aspiring superhero.]]></description><link>https://blog.leonhassan.co.uk/</link><image><url>https://blog.leonhassan.co.uk/favicon.png</url><title>Leon Hassan</title><link>https://blog.leonhassan.co.uk/</link></image><generator>Ghost 5.85</generator><lastBuildDate>Tue, 14 Apr 2026 01:35:43 GMT</lastBuildDate><atom:link href="https://blog.leonhassan.co.uk/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[duty-free.cc]]></title><description><![CDATA[<figure class="kg-card kg-image-card"><img src="https://blog.leonhassan.co.uk/content/images/2026/04/image.png" class="kg-image" alt="duty-free.cc" loading="lazy"></figure><!--kg-card-begin: markdown--><p>forum.duty-free.cc is a Russian-language forum focused on information security and hacking-related topics, where users discuss penetration testing, vulnerabilities, exploits, OSINT, and real-world cases. The platform presents itself as a community of cybersecurity professionals and enthusiasts who share experience, publish articles, and discuss methods of defense and attack; however,</p>]]></description><link>https://blog.leonhassan.co.uk/duty-free-cc/</link><guid isPermaLink="false">69dd968e9892ac09c66d06e9</guid><dc:creator><![CDATA[Leon Hassan]]></dc:creator><pubDate>Tue, 14 Apr 2026 01:21:18 GMT</pubDate><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://blog.leonhassan.co.uk/content/images/2026/04/image.png" class="kg-image" alt="duty-free.cc" loading="lazy"></figure><!--kg-card-begin: markdown--><p>forum.duty-free.cc is a Russian-language forum focused on information security and hacking-related topics, where users discuss penetration testing, vulnerabilities, exploits, OSINT, and real-world cases. The platform presents itself as a community of cybersecurity professionals and enthusiasts who share experience, publish articles, and discuss methods of defense and attack; however, it exists at the intersection of legitimate security research and the underground scene, so topics related to gray or controversial practices may also appear.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Consuming SAP Gateway services on localhost]]></title><description><![CDATA[<p>All web developers will at some point come across &quot;CORS&quot; errors and they can be incredibly frustrating, after all you&apos;re just trying to grab some data from your development server. This blog will show you how you can use XAMPP as a reverse-proxy so that you</p>]]></description><link>https://blog.leonhassan.co.uk/consuming-sap-gateway-services-on-localhost/</link><guid isPermaLink="false">65d487edb18ec01533660a31</guid><category><![CDATA[SAP]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Fiori]]></category><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Leon Hassan]]></dc:creator><pubDate>Tue, 25 Jun 2019 11:30:00 GMT</pubDate><media:content url="https://blog.leonhassan.co.uk/content/images/2019/06/SAP_2011_logo.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.leonhassan.co.uk/content/images/2019/06/SAP_2011_logo.svg" alt="Consuming SAP Gateway services on localhost"><p>All web developers will at some point come across &quot;CORS&quot; errors and they can be incredibly frustrating, after all you&apos;re just trying to grab some data from your development server. This blog will show you how you can use XAMPP as a reverse-proxy so that you can pull data from your development server to your local machine.</p><h2 id="cross-origin-resource-sharing-cors-">Cross-Origin Resource Sharing (CORS)</h2><p>In order to understand CORS, we need to understand why it exists and that brings us to the <a href="https://www.w3.org/Security/wiki/Same_Origin_Policy?ref=blog.leonhassan.co.uk">Same-Origin policy</a> (where an origin can be equated to a domain). The general principal behind this policy is that a 3rd-party should not be able to interfere with your webapp via means such as Cross-Site Request Forgeries (CSRF) and clickjacking, in essence it protects the integrity of data contained and transmitted from all origins.</p><p>However without being able to share things, how do we have the internet? CORS restricts the sharing of resources across origins so that it&apos;s possible to share resources without making yourself vulnerable to attacks. This is the reason that your local webapp running on <code>localhost:8080</code> can&apos;t pull data from a gateway service running on <code>https://my.gateway/my-service</code>, you&apos;re not on the same origin!</p><h2 id="what-s-a-reverse-proxy">What&apos;s a Reverse Proxy?</h2><p>A reverse proxy is the usual answer to this problem for web developers, it is essentially a server that sits in front of a web server and client requests to the web server. It sits between a client and a server as the middle-man, in our particular case this means that it becomes the single-point of origin for all our requests.</p><p>By running a local web server on our environment and routing all of our traffic through the server we can trick a browser into thinking that our webapp and gateway are running on the <em>same</em> origin.</p><p>I&apos;ll be using <a href="https://www.apachefriends.org/index.html?ref=blog.leonhassan.co.uk">XAMPP</a> as my web server, download it <a href="https://www.apachefriends.org/index.html?ref=blog.leonhassan.co.uk">here</a>.</p><h2 id="xampp-configuration">XAMPP configuration</h2><p>After installing XAMPP on your machine, open up the control panel for it and start up your Apache server. If you visit <a href="localhost:8080">localhost:8080</a> you&apos;ll notice that it resolves to something you&apos;re not used to seeing, this is your Apache web server!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.leonhassan.co.uk/content/images/2019/06/Apache-web-server-defaults.png" class="kg-image" alt="Consuming SAP Gateway services on localhost" loading="lazy"><figcaption>Apache web server defaults</figcaption></figure><p><em>Note</em>: You may have to enable default port forwarding to point your web server to localhost.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.leonhassan.co.uk/content/images/2019/06/Apache-port-forwarding-defaults.png" class="kg-image" alt="Consuming SAP Gateway services on localhost" loading="lazy"><figcaption>Networking settings: Port forwarding rules</figcaption></figure><h3 id="running-your-webapp">Running your webapp</h3><p>In order to host your webapp on your fresh new web server, you can either:</p><ul><li>Move your web projects into Apache&apos;s default hosting directory which is <code>/ApacheInstall/apache/htdocs</code></li><li>Update your Apache configuration to point to your project directory e.g. <code>~/Documents/</code></li></ul><p>You can update the configuration by clicking on the <em>config</em> button on the XAMPP control panel, once <code>httpd.conf</code> is open you can use Ctrl + F for &quot;htdocs&quot; to find the line you want to edit.</p><p><em>Note</em>: On Mac you will have to mount a volume in order to find the XAMPP config files.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.leonhassan.co.uk/content/images/2019/06/Apache-mount-volume.png" class="kg-image" alt="Consuming SAP Gateway services on localhost" loading="lazy"><figcaption>Mounting a volume in XAMPP on Mac</figcaption></figure><p>Restart your web server and edit the URL to point to one of your projects (or delete everything else and you&apos;ll see a file tree with all your projects) and you should see your webapp.</p><h3 id="reverse-proxy-configuration">Reverse Proxy configuration</h3><p>Now that we&apos;ve got our webapps running on our web server, we need to pull our data source into the server too. To do this we need to enable a few Apache modules in the <code>httpd-apache.conf</code> and then set-up the proxy path, this is the URL-pattern we want to map from our server to our gateway server.</p><p>Activate the following modules:</p><pre><code class="language-bash">Include &quot;conf/extra/httpd-proxy.conf&quot;
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so</code></pre><p><em>Note</em>: # is used to comment lines in our config files, you can search for these particular modules and uncomment them if you want to save yourself any headaches that may arise from updates to XAMPP.</p><p>With these modules loaded onto our web server, we can now set-up our proxy paths. This is how we map a specific URL-pattern on our web server to our gateway server.</p><p>If you&apos;re using an SAP Gateway server, then it makes sense to map anything after <code>./sap/</code> in our URL to our Gateway server. Add the below lines to your config:</p><pre><code class="language-bash">ProxyPass /sap/ https://my-gateway-box.com/sap/
ProxyPassReverse /sap/ https://my-gateway-box.com/sap/</code></pre><p><em>Note</em>: Update <em>my-gateway-box</em> in the above code, to your own gateway server!</p><p>Now save your config changes, restart Apache. Our configuration is now live and that means that a service call from our web server that looks something like <code>localhost:8080/sap/opu/odata/sap/ZTEST_SERVICE</code> will be mapped to <code>https://my-gateway-box.com/sap/opu/odata/sap/ZTEST_SERVICE</code>. </p><p>Now load up a webapp that pulls data from your gateway server, you should see the data you&apos;ve been waiting for!</p><h2 id="leave-a-comment">Leave a comment</h2><p>If you&apos;ve gotten stuck somewhere along the way or you&apos;re using another web server as a reverse proxy, leave a comment and let me know how you got on.</p>]]></content:encoded></item><item><title><![CDATA[Deploying a React application to GitHub Pages]]></title><description><![CDATA[<p>This post will detail the process to easily and reliably deploy your React web app to GitHub Pages.</p><h2 id="what-is-github-pages">What is GitHub Pages?</h2><p><a href="https://pages.github.com/?ref=blog.leonhassan.co.uk">GitHub Pages</a><a> </a>is a way for you to turn your GitHub repository into a website for free, it hosts the site/app/documentation for your repository. It&apos;</p>]]></description><link>https://blog.leonhassan.co.uk/deploying-a-react-app-to-github-pages/</link><guid isPermaLink="false">65d487edb18ec01533660a2e</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Technology]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Leon Hassan]]></dc:creator><pubDate>Wed, 09 Jan 2019 12:30:00 GMT</pubDate><media:content url="https://blog.leonhassan.co.uk/content/images/2019/01/react-1.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.leonhassan.co.uk/content/images/2019/01/react-1.svg" alt="Deploying a React application to GitHub Pages"><p>This post will detail the process to easily and reliably deploy your React web app to GitHub Pages.</p><h2 id="what-is-github-pages">What is GitHub Pages?</h2><p><a href="https://pages.github.com/?ref=blog.leonhassan.co.uk">GitHub Pages</a><a> </a>is a way for you to turn your GitHub repository into a website for free, it hosts the site/app/documentation for your repository. It&apos;s free and helps you get on with developing rather than going through web hosting provider #342.</p><h2 id="pre-requisites">Pre-requisites</h2><p>You will need to have the following before starting this tutorial:</p><ul><li>Code editor (I use <a href="https://code.visualstudio.com/?ref=blog.leonhassan.co.uk">VS Code</a>)</li><li><a href="https://git-scm.com/?ref=blog.leonhassan.co.uk">Git SCM</a></li><li><a href="https://nodejs.org/en/download/?ref=blog.leonhassan.co.uk">Node.js</a></li><li><a href="https://www.npmjs.com/?ref=blog.leonhassan.co.uk">NPM</a> or <a href="https://yarnpkg.com/?ref=blog.leonhassan.co.uk">Yarn</a></li><li>A <a href="https://github.com/?ref=blog.leonhassan.co.uk">GitHub</a> account</li></ul><h2 id="creating-a-github-repository">Creating a GitHub repository</h2><p>The next step is creating an online repository for your application, log in to <a href="https://github.com/?ref=blog.leonhassan.co.uk">GitHub</a><a> </a>and then create a repository and fill out the details as below:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.leonhassan.co.uk/content/images/2019/01/create-a-git-repo.png" class="kg-image" alt="Deploying a React application to GitHub Pages" loading="lazy"><figcaption>Creating a GitHub repository</figcaption></figure><p>Once you have created the repository we need to turn GitHub Pages on for the repository. To do this navigate to the &quot;Settings&quot; tab of your repository and scroll down to the &quot;GitHub Pages&quot; section. Now set the source for your pages to <code>gh-pages branch</code> and save your changes.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.leonhassan.co.uk/content/images/2019/01/GitHub-Pages-settings.png" class="kg-image" alt="Deploying a React application to GitHub Pages" loading="lazy"><figcaption>GitHub Repository settings</figcaption></figure><p>Once you have saved your changes you will need to clone it to your development machine. You can get the clone URL (via SSH or HTTP) from your repository.</p><pre><code>git clone git@github.com:Leoooob/leonhassan.co.uk.git</code></pre><p>Now that we&apos;ve got our local repository set-up, we can create our application.</p><h2 id="creating-a-react-application">Creating a React application</h2><p>You are going to create a simple react application that is ready to go, to do this you are going to use the <code>create-react-app</code> NPM module.</p><pre><code>npm install -g create-react-app</code></pre><p>Now that you have installed the module, you want to use it to create the template application. I&apos;m going to use <code>leonhassan.co.uk</code> as my example project, once it&apos;s created you should navigate to the app directory and run it.</p><pre><code>npx create-react-app leonhassan.co.uk
cd leonhassan.co.uk
npm start</code></pre><p>You should now have be staring at a webpage served from <a href="localhost:3000">localhost:3000</a> that looks something like the below:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.leonhassan.co.uk/content/images/2019/01/create-react-app-screenshot.png" class="kg-image" alt="Deploying a React application to GitHub Pages" loading="lazy"><figcaption>A screenshot of the generated application</figcaption></figure><p>If you can see something similar to the screenshot then commit the changes to your repository and push them upstream.</p><pre><code>git add .
git commit -m &quot;create-react-app commit&quot;
git push origin master</code></pre><h2 id="deploying-to-github-pages">Deploying to GitHub Pages</h2><p>There is a very handy NPM module called <a href="https://www.npmjs.com/package/gh-pages?ref=blog.leonhassan.co.uk">gh-pages</a> that we are going to use to deploy the application for us by running a single command. </p><pre><code>npm install --save gh-pages</code></pre><p>While the package installs, edit your <code>package.json</code> to include 3 new key-value pairs, as highlighted below:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.leonhassan.co.uk/content/images/2019/01/package-json-example.png" class="kg-image" alt="Deploying a React application to GitHub Pages" loading="lazy"><figcaption>Screenshot of my updated package.json</figcaption></figure><p><em>Make sure that you use your own github.io repository link!</em></p><p>Now that we&apos;ve installed the deployment package and configured the project to use it properly, we can deploy our application by running the following command from the project folder.</p><pre><code>npm run deploy</code></pre><p>Once the deployment process has run it&apos;s course, you should be looking at a console output similar to this:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.leonhassan.co.uk/content/images/2019/01/example-deployment-output.png" class="kg-image" alt="Deploying a React application to GitHub Pages" loading="lazy"><figcaption>Example Deployment log</figcaption></figure><p>You have now successfully deployed to your GitHub page, well done! I recommend waiting a few minutes before checking the site, it can take a little while to build your webpage so don&apos;t be disheartened if it&apos;s not instantly available.</p><h2 id="leave-a-comment">Leave a comment</h2><p>If this tutorial has helped you or you have any questions, please leave a comment and share the post.</p>]]></content:encoded></item><item><title><![CDATA[UI5, Babel and Gulp. The beginnings of a DevOps pipeline.]]></title><description><![CDATA[<figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.leonhassan.co.uk/content/images/2018/10/babel-logo.svg" width="300" height="136" loading="lazy" alt></div><div class="kg-gallery-image"><img src="https://blog.leonhassan.co.uk/content/images/2018/10/gulp-logo.svg" width="175" height="387" loading="lazy" alt></div></div></div><figcaption>Babel.js and Gulp.js logos</figcaption></figure><p>In this article I will be detailing a very simple DevOps pipeline that uses <a href="http://babeljs.io/?ref=blog.leonhassan.co.uk">Babel.js</a> and <a href="https://gulpjs.com/?ref=blog.leonhassan.co.uk">Gulp.js</a> to transpile ECMAScript 6 to ES5, making it compatible with IE9!</p><p><em>Prerequisites</em>: Node.js v8.11</p><h2 id="npm-install">npm install</h2><p>Add the following dev dependencies to your</p>]]></description><link>https://blog.leonhassan.co.uk/ui5-babel-and-gulp-the-beginning-of-a-devops-pipeline/</link><guid isPermaLink="false">65d487edb18ec01533660a2c</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[OpenUI5]]></category><category><![CDATA[Technology]]></category><category><![CDATA[DevOps]]></category><dc:creator><![CDATA[Leon Hassan]]></dc:creator><pubDate>Tue, 09 Oct 2018 11:30:00 GMT</pubDate><media:content url="https://blog.leonhassan.co.uk/content/images/2018/10/OpenUI5_H.svg" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.leonhassan.co.uk/content/images/2018/10/babel-logo.svg" width="300" height="136" loading="lazy" alt="UI5, Babel and Gulp. The beginnings of a DevOps pipeline."></div><div class="kg-gallery-image"><img src="https://blog.leonhassan.co.uk/content/images/2018/10/gulp-logo.svg" width="175" height="387" loading="lazy" alt="UI5, Babel and Gulp. The beginnings of a DevOps pipeline."></div></div></div><figcaption>Babel.js and Gulp.js logos</figcaption></figure><img src="https://blog.leonhassan.co.uk/content/images/2018/10/OpenUI5_H.svg" alt="UI5, Babel and Gulp. The beginnings of a DevOps pipeline."><p>In this article I will be detailing a very simple DevOps pipeline that uses <a href="http://babeljs.io/?ref=blog.leonhassan.co.uk">Babel.js</a> and <a href="https://gulpjs.com/?ref=blog.leonhassan.co.uk">Gulp.js</a> to transpile ECMAScript 6 to ES5, making it compatible with IE9!</p><p><em>Prerequisites</em>: Node.js v8.11</p><h2 id="npm-install">npm install</h2><p>Add the following dev dependencies to your project by either dropping them directly into your <code>package.json</code> or by running <code>npm install --save-dev ${package}</code>, where <code>${package}</code> is the package name e.g. <code>@babel/core</code>.</p><pre><code>{
&#xA0; &quot;main&quot;: &quot;index.html&quot;,
&#xA0; &quot;devDependencies&quot;: {
&#xA0;&#xA0;&#xA0; &quot;@babel/core&quot;: &quot;^7.1.2&quot;,
&#xA0;&#xA0;&#xA0; &quot;@babel/preset-env&quot;: &quot;^7.1.0&quot;,
&#xA0;&#xA0;&#xA0; &quot;babel-cli&quot;: &quot;^6.26.0&quot;,
&#xA0;&#xA0;&#xA0; &quot;gulp&quot;: &quot;^3.9.1&quot;,
&#xA0;&#xA0;&#xA0; &quot;gulp-babel&quot;: &quot;^8.0.0&quot;,
&#xA0;&#xA0;&#xA0; &quot;gulp-rename&quot;: &quot;^1.4.0&quot;
&#xA0; }
}
</code></pre><p>Once you have all of these packages successfully installed, run the command <code>npm install</code> from the root directory.</p><h2 id="gulp-tasks">Gulp tasks</h2><p>Now create a <code>gulpfile</code> (terminal command <code>touch gulpfile.js</code>) at the root directory of your project and paste the below:</p><pre><code>const gulp = require(&apos;gulp&apos;);
const rename = require(&apos;gulp-rename&apos;);
const babel = require(&apos;gulp-babel&apos;);

// default gulp task
gulp.task(&apos;default&apos;, () =&gt;
  // for each js file in the javascript folder
&#xA0; gulp.src(&apos;./javascript/*.js&apos;)
&#xA0;&#xA0;&#xA0; .pipe(babel({
&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; // transpile ES6 to ES5
&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; presets: [&apos;@babel/preset-env&apos;]
&#xA0;&#xA0;&#xA0; }))
&#xA0;&#xA0;&#xA0; .pipe(rename((path) =&gt; {
      // add file name suffix .controller to file
&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; path.basename += &apos;.controller&apos;;
&#xA0;&#xA0;&#xA0; }))
    // save new js file to the controller folder
&#xA0;&#xA0;&#xA0; .pipe(gulp.dest(&apos;./controller&apos;))
);
</code></pre><p>To run this build pipeline, run the <code>gulp</code> command from the top-level directory of the project (where your <code>gulpfile.js</code> sits). </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.leonhassan.co.uk/content/images/2018/10/Screenshot-2018-10-03-at-15.43.00.png" class="kg-image" alt="UI5, Babel and Gulp. The beginnings of a DevOps pipeline." loading="lazy"><figcaption>example project file structure</figcaption></figure><h2 id="example-javascript-in-project">Example JavaScript in project</h2><p>Currently this simple build script:</p><ul><li>Takes each javascript file from the <code>javascript</code> folder named.</li><li>Transpiles them to ES5 as per the preset-env (you can write your own preset)</li><li>Adds the <code>.controller</code> suffix to the new file</li><li>Saves the new file in the <code>controller</code> folder</li></ul><h2 id="git-and-ui5-ignores">git and ui5 ignores</h2><p>Your project <code>.gitignore</code> should include:</p><pre><code>/node_modules
/controller
</code></pre><p>Your project <code>.Ui5RepositoryIgnore</code> should include:</p><pre><code>/node_modules
/javascript
gulpfile.js
package.json
package-lock.json
</code></pre><h2 id="leave-a-comment">Leave a comment</h2><p>I hope that this tutorial has been helpful to you in modernising your UI5 workflow, I&apos;ve setup an example repository <a href="https://github.com/Leoooob/SAPUI5_DevOps?ref=blog.leonhassan.co.uk">here</a> on GitHub. Leave a comment or tweet me with your thoughts and questions!</p>]]></content:encoded></item><item><title><![CDATA[Progressive Web Applications Manifest and Cache API]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In this PWA post I will cover the Cache API and the storage limitations of the Cache and IndexedDB APIs, how you can use a Manifest file to support touch icons and the &apos;add to homescreen&apos; functionality and the <a href="https://developers.google.com/web/tools/lighthouse/?ref=blog.leonhassan.co.uk">Lighthouse</a> audit tool.</p>
<h2 id="cacheapi">Cache API</h2>
<p>A cache is technically</p>]]></description><link>https://blog.leonhassan.co.uk/progressive-web-applications-manifest/</link><guid isPermaLink="false">65d487edb18ec01533660a2a</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[PWA]]></category><category><![CDATA[Technology]]></category><category><![CDATA[Javascript]]></category><dc:creator><![CDATA[Leon Hassan]]></dc:creator><pubDate>Tue, 13 Mar 2018 12:00:00 GMT</pubDate><media:content url="https://blog.leonhassan.co.uk/content/images/2018/03/PWA-unofficial-logo.svg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.leonhassan.co.uk/content/images/2018/03/PWA-unofficial-logo.svg" alt="Progressive Web Applications Manifest and Cache API"><p>In this PWA post I will cover the Cache API and the storage limitations of the Cache and IndexedDB APIs, how you can use a Manifest file to support touch icons and the &apos;add to homescreen&apos; functionality and the <a href="https://developers.google.com/web/tools/lighthouse/?ref=blog.leonhassan.co.uk">Lighthouse</a> audit tool.</p>
<h2 id="cacheapi">Cache API</h2>
<p>A cache is technically a request-to-response map, where we store request URLs and their responses in the cache. This allows us to serve data from the cache initially, or intercept network requests and serve directly from the cache where possible.</p>
<p>There are a number of methods in the <a href="https://www.w3.org/TR/service-workers/?ref=blog.leonhassan.co.uk#cache">Cache API</a>, however I will not be covering them in this post.</p>
<h3 id="buildingasimpleapplicationcache">Building a simple application cache</h3>
<p>Mapping our cache actions to Service Worker events will help us to understand how to incorporate caching into service workers and allow you to build upon your service workers easily.</p>
<table>
<thead>
<tr>
<th>SW event</th>
<th>Cache action</th>
</tr>
</thead>
<tbody>
<tr>
<td>Install</td>
<td>Build cache</td>
</tr>
<tr>
<td>Activate</td>
<td>Update cache</td>
</tr>
<tr>
<td>Fetch</td>
<td>Retreive asset</td>
</tr>
</tbody>
</table>
<p>There are a number of common caching strategies such as the Cache with Network fallback you have seen in a <a href="https://blog.leonhassan.co.uk/2018/01/12/service-workers-in-progressive-web-apps/">previous PWA post</a>. This strategy focuses on caching on network response, other strategies focus on caching on installation or on user interaction.</p>
<p><em><strong>Note</strong></em>: You should have a service worker setup prior to continuing. Instructions to set-up a basic service worker and how to implement it on your page can be found on my <a href="http://links.leonhassan.co.uk/ExampleSW?ref=blog.leonhassan.co.uk">GitHub Gist</a>.</p>
<h3 id="cacheoninstall">Cache on Install</h3>
<p>Caching on install happens in almost every service worker, because we almost <em>always</em> cache some static assets. For instance a website header image, the CSS file etc. This particular caching stategy extends to webapps in a specific way, caching your application shell! By caching our application shell we stop the user from having to redownload the static parts of a web application, meaning that they only use data for the dynamic parts of the application.</p>
<p><img src="https://blog.leonhassan.co.uk/content/images/2018/02/Cache-on-install-1.svg" alt="Progressive Web Applications Manifest and Cache API" loading="lazy"></p>
<p>The code example for this can be found on my <a href="http://links.leonhassan.co.uk/ExampleSW?ref=blog.leonhassan.co.uk">GitHub Gist</a>.</p>
<h3 id="cacheonuserinteraction">Cache on User Interaction</h3>
<p>Caching on user interaction is very useful for a lot of user journeys, for instance a blog that does not fully-support an offline experience could make use of this strategy by allowing users to &quot;Save for Later&quot;. Triggering this action would then put a copy of an article/post in the cache, allowing the user to visit that post at their leisure e.g. while on the train home from work, while offline.</p>
<p>The Cache API is available from pages, not just the service worker and this means that we can add things to the cache without involving the service worker. The following code is an example DOM structure and JavaScript that should be added to your webapp&apos;s JavaScript and <strong>not</strong> your service worker:</p>
<p><em>HTML</em>:</p>
<pre><code class="language-html">&lt;div id=&quot;articleList&quot; &gt;
  &lt;div class=&quot;article&quot; id=&quot;articleID&quot;&gt;
    &lt;!-- your article link/thumbnail goes here --&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p><em>JavaScript</em>:</p>
<pre><code class="language-javascript">// when an item in your articleList is clicked
document.querySelector(&apos;#articleList&apos;).addEventListener(&apos;click&apos;, (event) =&gt; {
  event.preventDefault();
  var class = event.target.class;
  if (class.includes(&quot;article&quot;)) {
    var id = event.target.id;
    
    // create a new cache for the article and add it
    caches.open(&apos;mysite-article-&apos; + id)
      .then((cache) =&gt; {
        // fetch the article to add to the response
        fetch(&apos;/get-article-urls?id=&apos; + id)
          .then((response) =&gt; return response.json())
          .then((urls) =&gt; cache.addAll(urls))
          .catch((error) =&gt; return error);
      }).catch((error) =&gt; sreturn error);
  }
});
</code></pre>
<p>Using the code example above you should be able to implement Cache on User Interaction with ease, the only real changes needed are configuration-related for your website e.g. DOM structure and fetch URL + parameters. You will also need to adjust your <code>fetch</code> event listener in the service worker to check for article caches.</p>
<h2 id="storagelimitations">Storage limitations</h2>
<p>Due to Progressive Web Application&apos;s being a rather young technology, the specifications for these young APIs aren&apos;t quite as unified across browsers as you would expect and therefore implementation details vary slightly. To clarify: there is <strong>no change in behaviour</strong> across browsers.</p>
<p>It is very common for these storage limitation to be across the various storage APIs, that means that your cache, indexedDB, localStorage and cookies etc can all count towards your storage limit.</p>
<table>
<thead>
<tr>
<th>Browser</th>
<th>Limitations</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Chrome &amp; Opera</td>
<td>No Limit</td>
<td>Storage is per origin, not API</td>
</tr>
<tr>
<td>Firefox</td>
<td>No Limit</td>
<td>Prompts after 50mb</td>
</tr>
<tr>
<td>Safari</td>
<td>No Limit</td>
<td>Prompts after 5mb</td>
</tr>
<tr>
<td>Internet Explorer (v10+)</td>
<td>250mb</td>
<td>Prompts after 10mb</td>
</tr>
<tr>
<td>Edge</td>
<td>50mb</td>
<td>Limitations are calculated from available space</td>
</tr>
</tbody>
</table>
<p>These are not hard figures as the specification is still being standardised across browsers, therefore they are subject to change. I would use the rule of thumb: Don&apos;t bank on more than 20mb of space available. Think of your mobile users, your poor storage starved mobile users!</p>
<hr>
<h2 id="manifestfile">Manifest file</h2>
<p>A manifest file is primarily for mobile browsers, allowing you to simulate a native-app experience for your web app. This is achieved by giving users the opportunity to &apos;Add to Homescreen&apos;. This will &apos;install&apos; the application on the user&apos;s device and allow them to run it from their app launcher, in a headerless browser. This gives the user a more native-feeling experience.</p>
<p><img src="https://blog.leonhassan.co.uk/content/images/2017/12/Native_App_wrapper_for_PWA_400x.png" alt="Progressive Web Applications Manifest and Cache API" loading="lazy"></p>
<p>The file is just some json information, here is an example:</p>
<pre><code class="language-javascript">{
  &quot;name&quot;: &quot;My Application&quot;,
  &quot;short_name&quot;: &quot;My Application&quot;,
  &quot;start_url&quot;: &quot;index.html&quot;,
  &quot;icons&quot;: [{
    &quot;src&quot;: &quot;images/touch/icon-128x128.png&quot;,
    &quot;sizes&quot;: &quot;128x128&quot;,
    &quot;type&quot;: &quot;image/png&quot;
  }],
  &quot;background_color&quot;: &quot;#ffffff&quot;,
  &quot;display&quot;: &quot;standalone&quot;,
  &quot;theme_color&quot;: &quot;#f05f40&quot;
}
</code></pre>
<p>You can examine your manifest file in Chrome Dev Tools, via the &apos;Application&apos; tab and Manifest heading. You can now examine many of the individual properties in your manifest, it will even render the icon images.</p>
<p><img src="https://blog.leonhassan.co.uk/content/images/2018/02/Manifest-dev-tools.png" alt="Progressive Web Applications Manifest and Cache API" loading="lazy"></p>
<p>You can include your manifest in your webapp by adding the following HTML to your head tag:</p>
<pre><code class="language-html">&lt;link rel=&quot;manifest&quot; href=&quot;manifest.json&quot;&gt;
</code></pre>
<h3 id="touchicons">Touch icons</h3>
<p>The <code>icons</code> key in your manifest file are important, especially as the manifest specification matures. These icons are used not just as the &apos;app launcher&apos; tile image, but also in the splash page of the app while it loads.</p>
<p>Currently for other non-Android operating systems, we need to specify some HTML shivs so that our icons are picked up. The below example uses the <a href="https://wandr.world/?ref=blog.leonhassan.co.uk">Wandr</a> website as an example:</p>
<pre><code class="language-html">  &lt;!-- Add to homescreen for Safari on iOS --&gt;
  &lt;meta name=&quot;apple-mobile-web-app-capable&quot; content=&quot;yes&quot;&gt;
  &lt;meta name=&quot;apple-mobile-web-app-status-bar-style&quot; content=&quot;black&quot;&gt;
  &lt;meta name=&quot;apple-mobile-web-app-title&quot; content=&quot;Wandr&quot;&gt;
  &lt;link rel=&quot;apple-touch-icon&quot; href=&quot;./assets/static/favicon_192px.png&quot;&gt;

  &lt;!-- Tile for Win8 --&gt;
  &lt;meta name=&quot;msapplication-TileColor&quot; content=&quot;#f05f40&quot;&gt;
  &lt;meta name=&quot;msapplication-TileImage&quot; content=&quot;./assets/static/favicon_192px.png&quot;&gt;
</code></pre>
<p>As Progressive Web Applications mature and become more widely supported, these shivs won&apos;t be necessary. You can read more about all the different properties you can set in your manifest at <a href="https://developers.google.com/web/fundamentals/web-app-manifest/?ref=blog.leonhassan.co.uk">Google Developer docs</a>.</p>
<h2 id="lighthouseaudittool">Lighthouse audit tool</h2>
<p>Lighthouse is an open-source audit tool for webpages, focusing on the core areas: progressive web apps (most of this comes from the <a href="https://developers.google.com/web/progressive-web-apps/checklist?ref=blog.leonhassan.co.uk">PWA checklist</a>), performance, accessibility and best practices. Lighthouse scores the webapp in each of these areas and provides human-friendly information on how to score higher and fix issues in your webapp.</p>
<p>You can run Lighthouse directly from Chrome dev. tools on a page (open Chrome dev tools and navigate to the <em>Audits</em> tab), as a <a href="https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=en&amp;ref=blog.leonhassan.co.uk">Chrome extension</a> or from the commandline using the <a href="https://www.npmjs.com/package/lighthouse?ref=blog.leonhassan.co.uk">npm package</a>.</p>
<p><img src="https://blog.leonhassan.co.uk/content/images/2018/03/pwa-lighthouse.png" alt="Progressive Web Applications Manifest and Cache API" loading="lazy"></p>
<p>You can run Lighthouse directly from Chrome dev. tools on a page, as a <a href="https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=en&amp;ref=blog.leonhassan.co.uk">Chrome extension</a> or from the commandline using the <a href="https://www.npmjs.com/package/lighthouse?ref=blog.leonhassan.co.uk">npm package</a>.</p>
<p>The best part of a Lighthouse report is the feedback given for each of your scores, the feedback tells you what was done correctly and what can be approved. The highlight of this is that the information is human-friendly, not just a generic error as we so regularly find in web development.</p>
<p><img src="https://blog.leonhassan.co.uk/content/images/2018/03/Wandr-PWA-Lighthouse-breakdown.png" alt="Progressive Web Applications Manifest and Cache API" loading="lazy"></p>
<p>As you can see in the screenshot above, the criteria your webapp is measured by are clearly displayed and in cases where it does not meet these criteria comprehensive errors are given, as depicted in the screenshot below:</p>
<p><img src="https://blog.leonhassan.co.uk/content/images/2018/03/Wandr-Performance-opportunities.png" alt="Progressive Web Applications Manifest and Cache API" loading="lazy"></p>
<p>Opportunities to improve the performance of my webapp are listed in the above screenshot, with clear instructions on how this can be achieved.</p>
<p>Using Lighthouse to audit your webapp helps you to contribute to a reach PWA ecosystem and ensures that you are building responsive, performant webapps. In cases where your webapp isn&apos;t as well-built as it could be, Lighthouse even offers insights on improving your webapp. I believe that Lighthouse is an invaluable tool that will become a part of all web developers toolkits in the near future, get ahead of the curve and start using it now!</p>
<hr>
<h2 id="whathaveyoubuilt">What have you built?</h2>
<p>You can view a large and growing list of Progressive Web Applications at <a href="https://outweb.io/?ref=blog.leonhassan.co.uk">https://outweb.io/</a> and <a href="https://pwa.rocks/?ref=blog.leonhassan.co.uk">https://pwa.rocks/</a>, you can even index your own applications on these websites!</p>
<p>Leave a comment or tweet me, letting me know what you&apos;ve been building throughout this post! I&apos;m a massive advocate of PWAs and can&apos;t wait for the day that the majority of websites leverage service workers and manifest files.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>