{"id":83086,"date":"2022-09-25T22:03:10","date_gmt":"2022-09-25T20:03:10","guid":{"rendered":"https:\/\/rmarketingdigital.com\/dev\/progressively-enhance-your-progressive-web-app\/"},"modified":"2020-09-16T18:08:04","modified_gmt":"2020-09-16T21:08:04","slug":"progressively-enhance-your-progressive-web-app","status":"publish","type":"post","link":"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/","title":{"rendered":"Am\u00e9liorez progressivement votre Progressive Web App"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_86 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Contenidos<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#Modern-JavaScript\" >Modern JavaScript<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#The-sample-app-Fugu-Greetings\" >The sample app: Fugu Greetings<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#Progressive-enhancement\" >Progressive enhancement<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#The-Native-File-System-API\" >The Native File System API<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#The-Web-Share-and-Web-Share-Target-APIs\" >The Web Share and Web Share Target APIs<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#The-Asynchronous-Clipboard-API\" >The Asynchronous Clipboard API<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#The-Badging-API\" >The Badging API<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#The-Periodic-Background-Sync-API\" >The Periodic Background Sync API<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#Notification-Triggers-API\" >Notification Triggers API<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#The-Wake-Lock-API\" >The Wake Lock API<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#The-Idle-Detection-API\" >The Idle Detection API<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#Closing\" >Closing<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/progressively-enhance-your-progressive-web-app\/#Acknowledgements\" >Acknowledgements<\/a><\/li><\/ul><\/nav><\/div>\n<p><\/p>\n<div>\n<header class=\"w-article-header\">\n<p class=\"w-article-header__subhead w-mb--non\">\n          Building for modern browsers and progressively enhancing like it&#8217;s 2003\n        <\/p>\n<p>\n          <time>Jun 29, 2020<\/time><br \/>\n            Updated <time>Jul 10, 2020<\/time>\n        <\/p>\n<div class=\"w-layout-container--narrow w-post-signpost\">\n  <span class=\"w-post-signpost__title\">Appears in:<\/span><br \/>\n  Progressive Web Apps\n<\/div>\n<\/header>\n<p>\n  <iframe loading=\"lazy\" title=\"Progressively enhancing like it\u2019s 2003\" width=\"1080\" height=\"608\" src=\"https:\/\/www.youtube.com\/embed\/NXCT3htg9nk?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/p>\n<p>Back in March 2003, <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/twitter.com\/nickf\">Nick Finck<\/a> and<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/twitter.com\/schampeo\">Steve Champeon<\/a> stunned the web design world<br \/>\nwith the concept of<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"http:\/\/www.hesketh.com\/publications\/inclusive_web_design_for_the_future\/\">progressive enhancement<\/a>,<br \/>\na strategy for web design that emphasizes loading core web page content first,<br \/>\nand that then progressively adds more nuanced<br \/>\nand technically rigorous layers of presentation and features on top of the content.<br \/>\nWhile in 2003, progressive enhancement was about using\u2014at the time\u2014modern<br \/>\nCSS features, unobtrusive JavaScript, and even just Scalable Vector Graphics.<br \/>\nProgressive enhancement in 2020 and beyond is about using<br \/>\nmodern browser capabilities.<\/p>\n<figure class=\"w-figure\">\n  <img alt='100002010000053c000003e8b978fe17e590bc9a-2242580' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/100002010000053C000003E8B978FE17E590BC9A-2242580.png' alt=\"Inclusive web design for the future with progressive enhancement. Title slide from Finck and Champeon's original presentation.\"><figcaption class=\"w-figcaption\">\n    Slide: Inclusive Web Design for the Future With Progressive Enhancement.<br \/>\n    (<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"http:\/\/www.hesketh.com\/publications\/inclusive_web_design_for_the_future\/\">Source<\/a>)<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"modern-javascript\"><span class=\"ez-toc-section\" id=\"Modern-JavaScript\"><\/span>Modern JavaScript <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Speaking of JavaScript, the browser support situation for the latest core ES\u00a02015 JavaScript<br \/>\nfeatures is great.<br \/>\nThe new standard includes promises, modules, classes, template literals, arrow functions, <code>let<\/code> and <code>const<\/code>,<br \/>\ndefault parameters, generators, the destructuring assignment, rest and spread, <code>Map<\/code>\/<code>Set<\/code>,<br \/>\n<code>WeakMap<\/code>\/<code>WeakSet<\/code>, and many more.<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/caniuse.com\/#feat=es6\">All are supported<\/a>.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000000000009c40000039ef0a6fe5c50e42dea-1991285' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000000000009C40000039EF0A6FE5C50E42DEA-1991285.png' alt=\"The CanIUse support table for ES6 features showing support across all major browsers.\"><figcaption class=\"w-figcaption\">\n    The ECMAScript 2015 (ES6) browser support table. (<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/caniuse.com\/#feat=es6\">Source<\/a>)<br \/>\n  <\/figcaption><\/figure>\n<p>Async functions, an ES\u00a02017 feature and one of my personal favorites,<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/caniuse.com\/#feat=async-functions\">can be used<\/a><br \/>\nin all major browsers.<br \/>\nThe <code>async<\/code> and <code>await<\/code> keywords enable asynchronous, promise-based behavior<br \/>\nto be written in a cleaner style, avoiding the need to explicitly configure promise chains.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000000000009c400000304a19ef0ff1d72987d-1299321' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000000000009C400000304A19EF0FF1D72987D-1299321.png' alt=\"The CanIUse support table for async functions showing support across all major browsers.\"><figcaption class=\"w-figcaption\">\n    The Async functions browser support table. (<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/caniuse.com\/#feat=async-functions\">Source<\/a>)<br \/>\n  <\/figcaption><\/figure>\n<p>And even super recent ES\u00a02020 language additions like<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/caniuse.com\/#feat=mdn-javascript_operators_optional_chaining\">optional chaining<\/a> and<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/caniuse.com\/#feat=mdn-javascript_operators_nullish_coalescing\">nullish coalescing<\/a><br \/>\nhave reached support really quickly. You can see a code sample below.<br \/>\nWhen it comes to core JavaScript features, the grass couldn&#8217;t be much greener than it<br \/>\nis today.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> adventurer <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span><br>name<span class=\"token operator\">:<\/span> <span class=\"token string\">'Alice'<\/span><span class=\"token punctuation\">,<\/span><br>cat<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">{<\/span><br>name<span class=\"token operator\">:<\/span> <span class=\"token string\">'Dinah'<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">log<\/span><span class=\"token punctuation\">(<\/span>adventurer<span class=\"token punctuation\">.<\/span>dog<span class=\"token operator\">?.<\/span>name<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">log<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0<\/span> <span class=\"token operator\">??<\/span> <span class=\"token number\">42<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<figure class=\"w-figure w-figure--fullbleed\">\n  <img alt='1000020100000640000003e810e16d93c747b2d5-1385902' src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/1000020100000640000003E810E16D93C747B2D5-1385902.png' alt=\"The iconic Windows XP green grass background image.\"><figcaption class=\"w-figcaption w-figcaption--fullbleed\">\n    The grass is green when it comes to core JavaScript features.<br \/>\n    (Microsoft product screenshot, used with<br \/>\n    <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/www.microsoft.com\/en-us\/legal\/intellectualproperty\/permissions\/default\">permission<\/a>.)<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"the-sample-app:-fugu-greetings\"><span class=\"ez-toc-section\" id=\"The-sample-app-Fugu-Greetings\"><\/span>The sample app: Fugu Greetings <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>For this article, I work with a simple PWA, called<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/tomayac.github.io\/fugu-greetings\/public\/\">Fugu Greetings<\/a><br \/>\n(<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/tomayac\/fugu-greetings\">GitHub<\/a>).<br \/>\nThe name of this app is a tip of the hat to Project Fugu \ud83d\udc21, an effort to give the web all<br \/>\nthe powers of native applications.<br \/>\nYou can read more about the project on its<br \/>\nlanding page.<\/p>\n<p>Fugu Greetings is a drawing app that lets you create virtual greeting cards, and send<br \/>\nthem to your loved ones. It exemplifies<br \/>\nPWA&#8217;s core concepts. It&#8217;s<br \/>\nreliable and fully offline enabled, so even if you don&#8217;t<br \/>\nhave a network, you can still use it. It&#8217;s also Installable<br \/>\nto a device&#8217;s home screen and integrates seamlessly with the operating system<br \/>\nas a stand-alone application.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000006a2f58b840608cea761-4971462' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000006A2F58B840608CEA761-4971462.png' alt=\"Fugu Greetings PWA with a drawing that resembles the PWA community logo.\"><figcaption class=\"w-figcaption\">\n    The <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/tomayac.github.io\/fugu-greetings\/public\/\">Fugu Greetings<\/a> sample app.<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"progressive-enhancement\"><span class=\"ez-toc-section\" id=\"Progressive-enhancement\"><\/span>Progressive enhancement <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>With this out of the way, it&#8217;s time to talk about <em>progressive enhancement<\/em>.<br \/>\nThe MDN Web Docs Glossary <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/Progressive_Enhancement\">defines<\/a><br \/>\nthe concept as follows:<\/p>\n<blockquote>\n<p>Progressive enhancement is a design philosophy that povides a baseline of<br \/>\nessential content and functionality to as many users as possible, while<br \/>\ndelivering the best possible experience only to users of the most modern<br \/>\nbrowsers that can run all the required code.<\/p>\n<p><a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Learn\/Tools_and_testing\/Cross_browser_testing\/Feature_detection\">Feature detection<\/a><br \/>\nis generally used to determine whether browsers can handle more modern functionality,<br \/>\nwhile <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/Polyfill\">polyfills<\/a><br \/>\nare often used to add missing features with JavaScript.<\/p>\n[\u2026]\n<p>Progressive enhancement is a useful technique that allows web developers to focus<br \/>\non developing the best possible websites while making those websites work<br \/>\non multiple unknown user agents.<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/Graceful_degradation\">Graceful degradation<\/a><br \/>\nis related, but is not the same thing and is often seen as going in the opposite direction<br \/>\nto progressive enhancement.<br \/>\nIn reality, both approaches are valid and can often complement one another.<\/p>\n<p>  <cite><br \/>\n    MDN contributors<br \/>\n  <\/cite>\n<\/p><\/blockquote>\n<p>Starting each greeting card from scratch can be really cumbersome.<br \/>\nSo why not have a feature that allows users to import an image, and start from there?<br \/>\nWith a traditional approach, you&#8217;d have used an<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/input\/file\"><code>&lt;input type=file&gt;<\/code><\/a><br \/>\nelement to make this happen.<br \/>\nFirst, you&#8217;d create the element, set its <code>type<\/code> to <code>'file'<\/code> and add MIME types to the <code>accept<\/code> property,<br \/>\nand then programmatically \u00abclick\u00bb it and listen for changes.<br \/>\nWhen you select an image, it is imported straight onto the canvas.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">importImage<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">return<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">Promise<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">(<\/span><span class=\"token parameter\">resolve<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> input <span class=\"token operator\">=<\/span> document<span class=\"token punctuation\">.<\/span><span class=\"token function\">createElement<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'input'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>input<span class=\"token punctuation\">.<\/span>type <span class=\"token operator\">=<\/span> <span class=\"token string\">'file'<\/span><span class=\"token punctuation\">;<\/span><br>input<span class=\"token punctuation\">.<\/span>accept <span class=\"token operator\">=<\/span> <span class=\"token string\">'image\/*'<\/span><span class=\"token punctuation\">;<\/span><br>input<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'change'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token function\">resolve<\/span><span class=\"token punctuation\">(<\/span>input<span class=\"token punctuation\">.<\/span>files<span class=\"token punctuation\">[<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>input<span class=\"token punctuation\">.<\/span><span class=\"token function\">click<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>When there&#8217;s an <em>import<\/em> feature, there probably should be an <em>export<\/em> feature<br \/>\nso users can save their greeting cards locally.<br \/>\nThe traditional way of saving files is to create an anchor link<br \/>\nwith a <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/a#download\"><code>download<\/code><\/a><br \/>\nattribute and with a blob URL as its <code>href<\/code>.<br \/>\nYou&#8217;d also programmatically \u00abclick\u00bb it to trigger the download,<br \/>\nand, to prevent memory leaks, hopefully not forget to revoke the blob object URL.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">exportImage<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">blob<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> a <span class=\"token operator\">=<\/span> document<span class=\"token punctuation\">.<\/span><span class=\"token function\">createElement<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'a'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>a<span class=\"token punctuation\">.<\/span>download <span class=\"token operator\">=<\/span> <span class=\"token string\">'fugu-greeting.png'<\/span><span class=\"token punctuation\">;<\/span><br>a<span class=\"token punctuation\">.<\/span>href <span class=\"token operator\">=<\/span> <span class=\"token constant\">URL<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">createObjectURL<\/span><span class=\"token punctuation\">(<\/span>blob<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>a<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'click'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">e<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token function\">setTimeout<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token constant\">URL<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">revokeObjectURL<\/span><span class=\"token punctuation\">(<\/span>a<span class=\"token punctuation\">.<\/span>href<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">30<\/span> <span class=\"token operator\">*<\/span> <span class=\"token number\">1000<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>a<span class=\"token punctuation\">.<\/span><span class=\"token function\">click<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>But wait a minute. Mentally, you haven&#8217;t \u00abdownloaded\u00bb a greeting card, you have<br \/>\n\u00absaved\u00bb it.<br \/>\nRather than showing you a \u00absave\u00bb dialog that lets you choose where to put the file,<br \/>\nthe browser has directly downloaded the greeting card without user interaction<br \/>\nand has put it straight into your Downloads folder. This isn&#8217;t great.<\/p>\n<p>What if there were a better way?<br \/>\nWhat if you could just open a local file, edit it, and then save the modifications,<br \/>\neither to a new file, or back to the original file that you had initially opened?<br \/>\nTurns out there is. The Native File System API<br \/>\nallows you to open and create files and<br \/>\ndirectories, as well as modify and save them .<\/p>\n<p>So how do I feature-detect an API?<br \/>\nThe Native File System API exposes a new method <code>window.chooseFileSystemEntries()<\/code>.<br \/>\nConsequently, I need to conditionally load different import and export modules depending on whether this method is available. I&#8217;ve shown how to do this below.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">loadImportAndExport<\/span> <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'chooseFileSystemEntries'<\/span> <span class=\"token keyword\">in<\/span> window<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>Promise<span class=\"token punctuation\">.<\/span><span class=\"token function\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">[<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/import_image.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/export_image.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">else<\/span> <span class=\"token punctuation\">{<\/span><br>Promise<span class=\"token punctuation\">.<\/span><span class=\"token function\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">[<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/import_image_legacy.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/export_image_legacy.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>But before I dive into the Native File System API details,<br \/>\nlet me just quickly highlight the progressive enhancement pattern here.<br \/>\nOn browsers that currently don&#8217;t support the Native File System API, I load the legacy scripts.<br \/>\nYou can see the network tabs of Firefox and Safari below.<\/p>\n<figure class=\"w-figure\">\n  <img alt='100002010000058c000000ca65613fec1d7fb3e2-1128402' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/100002010000058C000000CA65613FEC1D7FB3E2-1128402.png' alt=\"Safari Web Inspector showing the legacy files getting loaded.\"><figcaption class=\"w-figcaption\">\n    Safari Web Inspector network tab.<br \/>\n  <\/figcaption><\/figure>\n<figure class=\"w-figure\">\n  <img alt='10000201000005800000012430b7b8786bf315dd-5981940' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000005800000012430B7B8786BF315DD-5981940.png' alt=\"Firefox Developer Tools showing the legacy files getting loaded.\"><figcaption class=\"w-figcaption\">\n    Firefox Developer Tools network tab.<br \/>\n  <\/figcaption><\/figure>\n<p>However, on Chrome, a browser that supports the API, only the new scripts are loaded.<br \/>\nThis is made elegantly possible thanks to<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/v8.dev\/features\/dynamic-import\">dynamic <code>import()<\/code><\/a>, which all modern browsers<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/caniuse.com\/#feat=es6-module-dynamic-import\">support<\/a>.<br \/>\nAs I said earlier, the grass is pretty green these days.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000006fc0000021ad16a28ba8f5cbf1a-3614115' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000006FC0000021AD16A28BA8F5CBF1A-3614115.png' alt=\"Chrome DevTools showing the modern files getting loaded.\"><figcaption class=\"w-figcaption\">\n    Chrome DevTools network tab.<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"the-native-file-system-api\"><span class=\"ez-toc-section\" id=\"The-Native-File-System-API\"><\/span>The Native File System API <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>So now that I have addressed this, it&#8217;s time to look at the actual implementation based on the Native File System API.<br \/>\nFor importing an image, I call <code>window.chooseFileSystemEntries()<\/code><br \/>\nand pass it an <code>accepts<\/code> property where I say I want image files.<br \/>\nBoth file extensions as well as MIME types are supported.<br \/>\nThis results in a file handle, from which I can get the actual file by calling <code>getFile()<\/code>.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">importImage<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> handle <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> window<span class=\"token punctuation\">.<\/span><span class=\"token function\">chooseFileSystemEntries<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><br>accepts<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><br><span class=\"token punctuation\">{<\/span><br>description<span class=\"token operator\">:<\/span> <span class=\"token string\">'Image files'<\/span><span class=\"token punctuation\">,<\/span><br>mimeTypes<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">'image\/*'<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span><br>extensions<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">'jpg'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'jpeg'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'png'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'webp'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'svg'<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">return<\/span> handle<span class=\"token punctuation\">.<\/span><span class=\"token function\">getFile<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>Exporting an image is almost the same, but this time<br \/>\nI need to pass a type parameter of <code>'save-file'<\/code> to the <code>chooseFileSystemEntries()<\/code> method.<br \/>\nFrom this I get a file save dialog.<br \/>\nWith file open, this wasn&#8217;t necessary since <code>'open-file'<\/code> is the default.<br \/>\nI set the <code>accepts<\/code> parameter similarly to before, but this time limited to just PNG images.<br \/>\nAgain I get back a file handle, but rather than getting the file,<br \/>\nthis time I create a writable stream by calling <code>createWritable()<\/code>.<br \/>\nNext, I write the blob, which is my greeting card image, to the file.<br \/>\nFinally, I close the writable stream.<\/p>\n<p>Everything can always fail: The disk could be out of space,<br \/>\nthere could be a write or read error, or maybe simply the user cancels the file dialog.<br \/>\nThis is why I always wrap the calls in a <code>try...catch<\/code> statement.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">exportImage<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">blob<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> handle <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> window<span class=\"token punctuation\">.<\/span><span class=\"token function\">chooseFileSystemEntries<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><br>type<span class=\"token operator\">:<\/span> <span class=\"token string\">'save-file'<\/span><span class=\"token punctuation\">,<\/span><br>accepts<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><br><span class=\"token punctuation\">{<\/span><br>description<span class=\"token operator\">:<\/span> <span class=\"token string\">'Image file'<\/span><span class=\"token punctuation\">,<\/span><br>extensions<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">'png'<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span><br>mimeTypes<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">'image\/png'<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">const<\/span> writable <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> handle<span class=\"token punctuation\">.<\/span><span class=\"token function\">createWritable<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">await<\/span> writable<span class=\"token punctuation\">.<\/span><span class=\"token function\">write<\/span><span class=\"token punctuation\">(<\/span>blob<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">await<\/span> writable<span class=\"token punctuation\">.<\/span><span class=\"token function\">close<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>Using progressive enhancement with the Native File System API,<br \/>\nI can open a file as before.<br \/>\nThe imported file is drawn right onto the canvas.<br \/>\nI can make my edits and finally save them with a real save dialog box<br \/>\nwhere I can choose the name and storage location of the file.<br \/>\nNow the file is ready to be preserved for eternity.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005db39851711e3cb3bf0-1180658' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005DB39851711E3CB3BF0-1180658.png' alt=\"Fugu Greetings app with a file open dialog.\"><figcaption class=\"w-figcaption\">\n    The file open dialog.<br \/>\n  <\/figcaption><\/figure>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005db01941257d7be6a85-4907083' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005DB01941257D7BE6A85-4907083.png' alt=\"Fugu Greetings app now with an imported image.\"><figcaption class=\"w-figcaption\">\n    The imported image.<br \/>\n  <\/figcaption><\/figure>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005dbcbe1b58ec828bb29-9444761' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005DBCBE1B58EC828BB29-9444761.png' alt=\"Fugu Greetings app with the modified image.\"><figcaption class=\"w-figcaption\">\n    Saving the modified image to a new file.<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"the-web-share-and-web-share-target-apis\"><span class=\"ez-toc-section\" id=\"The-Web-Share-and-Web-Share-Target-APIs\"><\/span>The Web Share and Web Share Target APIs <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Apart from storing for eternity, maybe I actually want to share my greeting card.<br \/>\nThis is something that the Web Share API and<br \/>\nWeb Share Target API allow me to do.<br \/>\nMobile, and more recently desktop operating systems have gained native sharing<br \/>\nmechanisms.<br \/>\nFor example, below is desktop Safari&#8217;s share sheet on macOS triggered from an article on<br \/>\nmy <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/blog.tomayac.com\/\">blog<\/a>.<br \/>\nWhen you click the <strong>Share Article<\/strong> button, you can share a link to the article with a friend, for<br \/>\nexample, via the native macOS Messages app.<\/p>\n<figure class=\"w-figure\">\n  <img alt='1000020100000356000001c434f5dd5d64721768-4082576' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/1000020100000356000001C434F5DD5D64721768-4082576.png' alt=\"Desktop Safari's share sheet on macOS triggered from an article's Share button\" width=\"600\"><figcaption class=\"w-figcaption\">\n    Web Share API on desktop Safari on macOS.<br \/>\n  <\/figcaption><\/figure>\n<p>The code to make this happen is pretty straightforward. I call <code>navigator.share()<\/code> and<br \/>\npass it an optional <code>title<\/code>, <code>text<\/code>, and <code>url<\/code> in an object.<br \/>\nBut what if I want to attach an image? Level\u00a01 of the Web Share API doesn&#8217;t support this yet.<br \/>\nThe good news is that Web Share Level\u00a02 has added file sharing capabilities.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span><span class=\"token function\">share<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><br>title<span class=\"token operator\">:<\/span> <span class=\"token string\">'Check out this article:'<\/span><span class=\"token punctuation\">,<\/span><br>text<span class=\"token operator\">:<\/span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`<\/span><span class=\"token string\">\"<\/span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${<\/span>document<span class=\"token punctuation\">.<\/span>title<span class=\"token interpolation-punctuation punctuation\">}<\/span><\/span><span class=\"token string\">\" by @tomayac:<\/span><span class=\"token template-punctuation string\">`<\/span><\/span><span class=\"token punctuation\">,<\/span><br>url<span class=\"token operator\">:<\/span> document<span class=\"token punctuation\">.<\/span><span class=\"token function\">querySelector<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'link[rel=canonical]'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>href<span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">warn<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>Let me show you how to make this work with the Fugu Greeting card application.<br \/>\nFirst, I need to prepare a <code>data<\/code> object with a <code>files<\/code> array consisting of one blob, and then<br \/>\na <code>title<\/code> and a <code>text<\/code>. Next, as a best practice, I use the new <code>navigator.canShare()<\/code> method which does<br \/>\nwhat its name suggests:<br \/>\nIt tells me if the <code>data<\/code> object I&#8217;m trying to share can technically be shared by the browser.<br \/>\nIf <code>navigator.canShare()<\/code> tells me the data can be shared, I&#8217;m ready to<br \/>\ncall <code>navigator.share()<\/code> as before.<br \/>\nBecause everything can fail, I&#8217;m again using a <code>try...catch<\/code> block.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">share<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">title<span class=\"token punctuation\">,<\/span> text<span class=\"token punctuation\">,<\/span> blob<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> data <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span><br>files<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><br><span class=\"token keyword\">new<\/span> <span class=\"token class-name\">File<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">[<\/span>blob<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">'fugu-greeting.png'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><br>type<span class=\"token operator\">:<\/span> blob<span class=\"token punctuation\">.<\/span>type<span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span><br>title<span class=\"token operator\">:<\/span> title<span class=\"token punctuation\">,<\/span><br>text<span class=\"token operator\">:<\/span> text<span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token operator\">!<\/span><span class=\"token punctuation\">(<\/span>navigator<span class=\"token punctuation\">.<\/span><span class=\"token function\">canShare<\/span><span class=\"token punctuation\">(<\/span>data<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">throw<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">Error<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">\"Can't share data.\"<\/span><span class=\"token punctuation\">,<\/span> data<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span><span class=\"token function\">share<\/span><span class=\"token punctuation\">(<\/span>data<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>As before, I use progressive enhancement.<br \/>\nIf both <code>'share'<\/code> and <code>'canShare'<\/code> exist on the <code>navigator<\/code> object, only then I go forward and<br \/>\nload <code>share.mjs<\/code> via dynamic <code>import()<\/code>.<br \/>\nOn browsers like mobile Safari that only fulfill one of the two conditions, I don&#8217;t load<br \/>\nthe functionality.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">loadShare<\/span> <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'share'<\/span> <span class=\"token keyword\">in<\/span> navigator <span class=\"token operator\">&amp;&amp;<\/span> <span class=\"token string\">'canShare'<\/span> <span class=\"token keyword\">in<\/span> navigator<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/share.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>In Fugu Greetings, if I tap the <strong>Share<\/strong> button on a supporting browser like Chrome on Android,<br \/>\nthe native share sheet opens.<br \/>\nI can, for example, choose Gmail, and the email composer widget pops up with the<br \/>\nimage attached.<\/p>\n<div class=\"w-columns\">\n<figure class=\"w-figure\">\n    <img alt='10000201000003e4000008004d3aea65db2aba6c-7000463' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000003E4000008004D3AEA65DB2ABA6C-7000463.png' alt=\"Native share sheet showing various apps to share the image to.\" width=\"300\"><figcaption class=\"w-figcaption\">\n      Choosing an app to share the file to.<br \/>\n    <\/figcaption><\/figure>\n<figure class=\"w-figure\">\n    <img alt='10000201000003e400000800d873c982e6d44c89-9083099' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000003E400000800D873C982E6D44C89-9083099.png' alt=\"Gmail's email compose widget with the image attached.\" width=\"300\"><figcaption class=\"w-figcaption\">\n      The file gets attached to a new email in Gmail&#8217;s composer.<br \/>\n    <\/figcaption><\/figure>\n<\/div>\n<p>Next, I want to talk about contacts, meaning a device&#8217;s address book<br \/>\nor contacts manager app.<br \/>\nWhen you write a greeting card, it may not always be easy to correctly write<br \/>\nsomeone&#8217;s name.<br \/>\nFor example, I have a friend Sergey who prefers his name to be spelled in Cyrillic letters. I&#8217;m<br \/>\nusing a German QWERTZ keyboard and have no idea how to type their name.<br \/>\nThis is a problem that the Contact Picker API can solve.<br \/>\nSince I have my friend stored in my phone&#8217;s contacts app,<br \/>\nvia the Contacts Picker API, I can tap into my contacts from the web.<\/p>\n<p>First, I need to specify the list of properties I want to access.<br \/>\nIn this case, I only want the names,<br \/>\nbut for other use cases I might be interested in telephone numbers, emails, avatar<br \/>\nicons, or physical addresses.<br \/>\nNext, I configure an <code>options<\/code> object and set <code>multiple<\/code> to <code>true<\/code>, so that I can select more<br \/>\nthan one entry.<br \/>\nFinally, I can call <code>navigator.contacts.select()<\/code>, which returns the desired properties<br \/>\nfor the user-selected contacts.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">getContacts<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> properties <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">'name'<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">const<\/span> options <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span> multiple<span class=\"token operator\">:<\/span> <span class=\"token boolean\">true<\/span> <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">return<\/span> <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>contacts<span class=\"token punctuation\">.<\/span><span class=\"token function\">select<\/span><span class=\"token punctuation\">(<\/span>properties<span class=\"token punctuation\">,<\/span> options<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>And by now you&#8217;ve probably learned the pattern:<br \/>\nI only load the file when the API is actually supported.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'contacts'<\/span> <span class=\"token keyword\">in<\/span> navigator<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/contacts.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>In Fugu Greeting, when I tap the <strong>Contacts<\/strong> button and select my two best pals,<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%91%D1%80%D0%B8%D0%BD,_%D0%A1%D0%B5%D1%80%D0%B3%D0%B5%D0%B9_%D0%9C%D0%B8%D1%85%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%D0%B8%D1%87\">\u0421\u0435\u0440\u0433\u0435\u0439 \u041c\u0438\u0445\u0430\u0439\u043b\u043e\u0432\u0438\u0447 \u0411\u0440\u0438\u043d<\/a> and <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/zh.wikipedia.org\/wiki\/%E6%8B%89%E9%87%8C%C2%B7%E4%BD%A9%E5%A5%87\">\u52b3\u4f26\u65af\u00b7\u7231\u5fb7\u534e\u00b7\u00bb\u62c9\u91cc\u00bb\u00b7\u4f69\u5947<\/a>,<br \/>\nyou can see how the<br \/>\ncontacts picker is limited to only show their names,<br \/>\nbut not their email addresses, or other information like their phone numbers.<br \/>\nTheir names are then drawn onto my greeting card.<\/p>\n<div class=\"w-columns\">\n<figure class=\"w-figure\">\n    <img alt='10000201000003e400000800ad376e3b4b3386c6-2969667' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000003E400000800AD376E3B4B3386C6-2969667.png' alt=\"Contacts picker showing the names of two contacts in the address book.\" width=\"300\"><figcaption class=\"w-figcaption\">\n      Selecting two names with the contact picker from the address book.<br \/>\n    <\/figcaption><\/figure>\n<figure class=\"w-figure\">\n    <img alt='1000000000000438000008ac945569e52ce934fa-7828050' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/1000000000000438000008AC945569E52CE934FA-7828050.png' alt=\"The names of the two previously picked contacts drawn on the greeting card.\" width=\"300\"><figcaption class=\"w-figcaption\">\n      The two names then get drawn onto the greeting card.<br \/>\n    <\/figcaption><\/figure>\n<\/div>\n<h2 id=\"the-asynchronous-clipboard-api\"><span class=\"ez-toc-section\" id=\"The-Asynchronous-Clipboard-API\"><\/span>The Asynchronous Clipboard API <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Up next is copying and pasting.<br \/>\nOne of our favorite operations as software developers is copy and paste.<br \/>\nAs a greeting card author, at times, I may want to do the same.<br \/>\nI may want to either paste an image into a greeting card I&#8217;m working on,<br \/>\nor copy my greeting card so I can continue editing it from<br \/>\nsomewhere else.<br \/>\nThe Async Clipboard API,<br \/>\nsupports both text and images.<br \/>\nLet me walk you through how I added copy and paste support to the Fugu<br \/>\nGreetings app.<\/p>\n<p>In order to copy something onto the system&#8217;s clipboard, I need to write to it.<br \/>\nThe <code>navigator.clipboard.write()<\/code> method takes an array of clipboard items as a<br \/>\nparameter.<br \/>\nEach clipboard item is essentially an object with a blob as a value, and the blob&#8217;s type<br \/>\nas the key.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">copy<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">blob<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>clipboard<span class=\"token punctuation\">.<\/span><span class=\"token function\">write<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">[<\/span><br><span class=\"token keyword\">new<\/span> <span class=\"token class-name\">ClipboardItem<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><br><span class=\"token punctuation\">[<\/span>blob<span class=\"token punctuation\">.<\/span>type<span class=\"token punctuation\">]<\/span><span class=\"token operator\">:<\/span> blob<span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>To paste, I need to loop over the clipboard items that I obtain by calling<br \/>\n<code>navigator.clipboard.read()<\/code>.<br \/>\nThe reason for this is that multiple clipboard items might be on the clipboard in<br \/>\ndifferent representations.<br \/>\nEach clipboard item has a <code>types<\/code> field that tells me the MIME types of the available<br \/>\nresources.<br \/>\nI call the clipboard item&#8217;s <code>getType()<\/code> method, passing the<br \/>\nMIME type I obtained before.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">paste<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> clipboardItems <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>clipboard<span class=\"token punctuation\">.<\/span><span class=\"token function\">read<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">for<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token keyword\">const<\/span> clipboardItem <span class=\"token keyword\">of<\/span> clipboardItems<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">for<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token keyword\">const<\/span> type <span class=\"token keyword\">of<\/span> clipboardItem<span class=\"token punctuation\">.<\/span>types<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> blob <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> clipboardItem<span class=\"token punctuation\">.<\/span><span class=\"token function\">getType<\/span><span class=\"token punctuation\">(<\/span>type<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">return<\/span> blob<span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>And it&#8217;s almost needless to say by now. I only do this on supporting browsers.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'clipboard'<\/span> <span class=\"token keyword\">in<\/span> navigator <span class=\"token operator\">&amp;&amp;<\/span> <span class=\"token string\">'write'<\/span> <span class=\"token keyword\">in<\/span> navigator<span class=\"token punctuation\">.<\/span>clipboard<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/clipboard.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>So how does this work in practice? I have an image open in the macOS Preview app and<br \/>\ncopy it to the clipboard.<br \/>\nWhen I click <strong>Paste<\/strong>, the Fugu Greetings app then asks me<br \/>\nwhether I want to allow the app to see text and images on the clipboard.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000006a2fe8ceb5ee2fb3f83-8002472' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000006A2FE8CEB5EE2FB3F83-8002472.png' alt=\"Fugu Greetings app showing the clipboard permission prompt.\"><figcaption class=\"w-figcaption\">\n    The clipboard permission prompt.<br \/>\n  <\/figcaption><\/figure>\n<p>Finally, after accepting the permission, the image is then pasted into the application.<br \/>\nThe other way round works, too.<br \/>\nLet me copy a greeting card to the clipboard.<br \/>\nWhen I then open Preview and click <strong>File<\/strong> and then <strong>New from Clipboard<\/strong>,<br \/>\nthe greeting card gets pasted into a new untitled image.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005a941a3287f1a5052a8-7523602' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005A941A3287F1A5052A8-7523602.png' alt=\"The macOS Preview app with an untitled, just pasted image.\"><figcaption class=\"w-figcaption\">\n    An image pasted into the macOS Preview app.<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"the-badging-api\"><span class=\"ez-toc-section\" id=\"The-Badging-API\"><\/span>The Badging API <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Another useful API is the Badging API.<br \/>\nAs an installable PWA, Fugu Greetings of course does have an app icon<br \/>\nthat users can place on the app dock or the home screen.<br \/>\nA fun and easy way to demonstrate the API is to (ab)use it in Fugu Greetings<br \/>\nas a pen strokes counter.<br \/>\nI have added an event listener that increments the pen strokes counter whenever the <code>pointerdown<\/code> event occurs<br \/>\nand then sets the updated icon badge.<br \/>\nWhenever the canvas gets cleared, the counter resets, and the badge is removed.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">let<\/span> strokes <span class=\"token operator\">=<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">;<\/span><p>canvas<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'pointerdown'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br>navigator<span class=\"token punctuation\">.<\/span><span class=\"token function\">setAppBadge<\/span><span class=\"token punctuation\">(<\/span><span class=\"token operator\">++<\/span>strokes<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><\/p><p>clearButton<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'click'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br>strokes <span class=\"token operator\">=<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">;<\/span><br>navigator<span class=\"token punctuation\">.<\/span><span class=\"token function\">setAppBadge<\/span><span class=\"token punctuation\">(<\/span>strokes<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><\/p><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>This feature is a progressive enhancement, so the loading logic is as usual.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'setAppBadge'<\/span> <span class=\"token keyword\">in<\/span> navigator<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/badge.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>In this example, I have drawn the numbers from one to seven, using one pen stroke<br \/>\nper number.<br \/>\nThe badge counter on the icon is now at seven.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005dbe9bf427abd63947a-3978772' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005DBE9BF427ABD63947A-3978772.png' alt=\"The numbers from one to seven drawn onto the greeting card, each with just one pen stroke.\"><figcaption class=\"w-figcaption\">\n    Drawing the numbers from 1 to 7, using seven pen strokes.<br \/>\n  <\/figcaption><\/figure>\n<figure class=\"w-figure\">\n  <img alt='10000201000002e6000001c0855aa2df1e30228c-3334591' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000002E6000001C0855AA2DF1E30228C-3334591.png' alt=\"Badge icon on the Fugu Greetings app showing the number 7.\" width=\"300\"><figcaption class=\"w-figcaption\">\n    The pen strokes counter in the form of the app icon badge.<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"the-periodic-background-sync-api\"><span class=\"ez-toc-section\" id=\"The-Periodic-Background-Sync-API\"><\/span>The Periodic Background Sync API <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Want to start each day fresh with something new?<br \/>\nA neat feature of the Fugu Greetings app is that it can inspire you each morning<br \/>\nwith a new background image to start your greeting card.<br \/>\nThe app uses the Periodic Background Sync API<br \/>\nto achieve this.<\/p>\n<p>The first step is to <em>register<\/em> a periodic sync event in the service worker registration.<br \/>\nIt listens for a sync tag called <code>'image-of-the-day'<\/code><br \/>\nand has a minimum interval of one day,<br \/>\nso the user can get a new background image every 24 hours.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">registerPeriodicBackgroundSync<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> registration <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>serviceWorker<span class=\"token punctuation\">.<\/span>ready<span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">try<\/span> <span class=\"token punctuation\">{<\/span><br>registration<span class=\"token punctuation\">.<\/span>periodicSync<span class=\"token punctuation\">.<\/span><span class=\"token function\">register<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'image-of-the-day-sync'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><br>minInterval<span class=\"token operator\">:<\/span> <span class=\"token number\">24<\/span> <span class=\"token operator\">*<\/span> <span class=\"token number\">60<\/span> <span class=\"token operator\">*<\/span> <span class=\"token number\">60<\/span> <span class=\"token operator\">*<\/span> <span class=\"token number\">1000<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">catch<\/span> <span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">error<\/span><span class=\"token punctuation\">(<\/span>err<span class=\"token punctuation\">.<\/span>name<span class=\"token punctuation\">,<\/span> err<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>The second step is to <em>listen<\/em> for the <code>periodicsync<\/code> event in the service worker.<br \/>\nIf the event tag is <code>'image-of-the-day'<\/code>, that is, the one that was registered before,<br \/>\nthe image of the day is retrieved via the <code>getImageOfTheDay()<\/code> function,<br \/>\nand the result propagated to all clients, so they can update their canvases and<br \/>\ncaches.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\">self<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'periodicsync'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">syncEvent<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>syncEvent<span class=\"token punctuation\">.<\/span>tag <span class=\"token operator\">===<\/span> <span class=\"token string\">'image-of-the-day-sync'<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>syncEvent<span class=\"token punctuation\">.<\/span><span class=\"token function\">waitUntil<\/span><span class=\"token punctuation\">(<\/span><br><span class=\"token punctuation\">(<\/span><span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> blob <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> <span class=\"token function\">getImageOfTheDay<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">const<\/span> clients <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> self<span class=\"token punctuation\">.<\/span>clients<span class=\"token punctuation\">.<\/span><span class=\"token function\">matchAll<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>clients<span class=\"token punctuation\">.<\/span><span class=\"token function\">forEach<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">(<\/span><span class=\"token parameter\">client<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br>client<span class=\"token punctuation\">.<\/span><span class=\"token function\">postMessage<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><br>image<span class=\"token operator\">:<\/span> blob<span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><br><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>Again this is truly a progressive enhancement, so the code is only loaded when the<br \/>\nAPI is supported by the browser.<br \/>\nThis applies to both the client code and the service worker code.<br \/>\nOn non-supporting browsers, neither of them is loaded.<br \/>\nNote how in the service worker, instead of a dynamic <code>import()<\/code><br \/>\n(that isn&#8217;t supported in a service worker context<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/w3c\/ServiceWorker\/issues\/1356#issuecomment-433411852\">yet<\/a>),<br \/>\nI use the classic<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/WorkerGlobalScope\/importScripts\"><code>importScripts()<\/code><\/a>.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><br><span class=\"token keyword\">const<\/span> registration <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>serviceWorker<span class=\"token punctuation\">.<\/span>ready<span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>registration <span class=\"token operator\">&amp;&amp;<\/span> <span class=\"token string\">'periodicSync'<\/span> <span class=\"token keyword\">in<\/span> registration<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/periodic_background_sync.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><br><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'periodicSync'<\/span> <span class=\"token keyword\">in<\/span> self<span class=\"token punctuation\">.<\/span>registration<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token function\">importScripts<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/image_of_the_day.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>In Fugu Greetings, pressing the <strong>Wallpaper<\/strong> button reveals the greeting card image of the day<br \/>\nthat is updated every day via the Periodic Background Sync API.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005e0b042853d0e77c224-9063496' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005E0B042853D0E77C224-9063496.png' alt=\"Fugu Greetings app with a new greeting card image of the day.\"><figcaption class=\"w-figcaption\">\n    Pressing the <strong>Wallpaper<\/strong> button displays the image of the day.<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"notification-triggers-api\"><span class=\"ez-toc-section\" id=\"Notification-Triggers-API\"><\/span>Notification Triggers API <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Sometimes even with a lot of inspiration, you need a nudge to finish a started greeting<br \/>\ncard.<br \/>\nThis is a feature that is enabled by the Notification Triggers API.<br \/>\nAs a user, I can enter a time when I want to be nudged to finish my greeting card.<br \/>\nWhen that time comes, I will get a notification that my greeting card is waiting.<\/p>\n<p>After prompting for the target time,<br \/>\nthe application schedules the notification with a <code>showTrigger<\/code>.<br \/>\nThis can be a <code>TimestampTrigger<\/code> with the previously selected target date.<br \/>\nThe reminder notification will be triggered locally, no network or server side is needed.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> targetDate <span class=\"token operator\">=<\/span> <span class=\"token function\">promptTargetDate<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>targetDate<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> registration <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>serviceWorker<span class=\"token punctuation\">.<\/span>ready<span class=\"token punctuation\">;<\/span><br>registration<span class=\"token punctuation\">.<\/span><span class=\"token function\">showNotification<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'Reminder'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><br>tag<span class=\"token operator\">:<\/span> <span class=\"token string\">'reminder'<\/span><span class=\"token punctuation\">,<\/span><br>body<span class=\"token operator\">:<\/span> <span class=\"token string\">\"It's time to finish your greeting card!\"<\/span><span class=\"token punctuation\">,<\/span><br>showTrigger<span class=\"token operator\">:<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">TimestampTrigger<\/span><span class=\"token punctuation\">(<\/span>targetDate<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>As with everything else I have shown so far, this is a progressive enhancement,<br \/>\nso the code is only conditionally loaded.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'Notification'<\/span> <span class=\"token keyword\">in<\/span> window <span class=\"token operator\">&amp;&amp;<\/span> <span class=\"token string\">'showTrigger'<\/span> <span class=\"token keyword\">in<\/span> <span class=\"token class-name\">Notification<\/span><span class=\"token punctuation\">.<\/span>prototype<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/notification_triggers.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>When I check the <strong>Reminder<\/strong> checkbox in Fugu Greetings, a prompt asks<br \/>\nme when I want to be reminded to finish my greeting card.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005db0d42f493acfd1b53-3464336' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005DB0D42F493ACFD1B53-3464336.png' alt=\"Fugu Greetings app with a prompt asking the user when they want to be reminded to finish their greeting card.\"><figcaption class=\"w-figcaption\">\n    Scheduling a local notification to be reminded to finish a greeting card.<br \/>\n  <\/figcaption><\/figure>\n<p>When a scheduled notification triggers in Fugu Greetings,<br \/>\nit is shown just like any other notification, but as I wrote before,<br \/>\nit didn&#8217;t require a network connection.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005e0b042853d0e77c225-6170541' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005E0B042853D0E77C225-6170541.png' alt=\"macOS Notification Center showing a triggered notification from Fugu Greetings.\" width=\"300\"><figcaption class=\"w-figcaption\">\n    The triggered notification appears in the macOS Notification Center.<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"the-wake-lock-api\"><span class=\"ez-toc-section\" id=\"The-Wake-Lock-API\"><\/span>The Wake Lock API <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I also want to include the Wake Lock API.<br \/>\nSometimes you just need to stare long enough at the screen until inspiration<br \/>\nkisses you.<br \/>\nThe worst that can happen then is for the screen to turn off.<br \/>\nThe Wake Lock API can prevent this from happening.<\/p>\n<p>The first step is to obtain a wake lock with the <code>navigator.wakelock.request method()<\/code>.<br \/>\nI pass it the string <code>'screen'<\/code> to obtain a screen wake lock.<br \/>\nI then add an event listener to be informed when the wake lock is released.<br \/>\nThis can happen, for example, when the tab visibility changes.<br \/>\nIf this happens, I can, when the tab becomes visible again, re-obtain the wake lock.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">let<\/span> wakeLock <span class=\"token operator\">=<\/span> <span class=\"token keyword\">null<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">requestWakeLock<\/span> <span class=\"token operator\">=<\/span> <span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br>wakeLock <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> navigator<span class=\"token punctuation\">.<\/span>wakeLock<span class=\"token punctuation\">.<\/span><span class=\"token function\">request<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'screen'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>wakeLock<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'release'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">log<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'Wake Lock was released'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">log<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'Wake Lock is active'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><p><span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">handleVisibilityChange<\/span> <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>wakeLock <span class=\"token operator\">!==<\/span> <span class=\"token keyword\">null<\/span> <span class=\"token operator\">&amp;&amp;<\/span> document<span class=\"token punctuation\">.<\/span>visibilityState <span class=\"token operator\">===<\/span> <span class=\"token string\">'visible'<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token function\">requestWakeLock<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span><\/p><p>document<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'visibilitychange'<\/span><span class=\"token punctuation\">,<\/span> handleVisibilityChange<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>document<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'fullscreenchange'<\/span><span class=\"token punctuation\">,<\/span> handleVisibilityChange<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><\/p><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>Yes, this is a progressive enhancement, so I only need to load it when the browser<br \/>\nsupports the API.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'wakeLock'<\/span> <span class=\"token keyword\">in<\/span> navigator <span class=\"token operator\">&amp;&amp;<\/span> <span class=\"token string\">'request'<\/span> <span class=\"token keyword\">in<\/span> navigator<span class=\"token punctuation\">.<\/span>wakeLock<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/wake_lock.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>In Fugu Greetings, there&#8217;s an <strong>Insomnia<\/strong> checkbox that, when checked, keeps the<br \/>\nscreen awake.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005dbcd9d10dd0a745315-7387536' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005DBCD9D10DD0A745315-7387536.png' alt=\"The insomnia checkbox, if checked, keeps the screen awake.\"><figcaption class=\"w-figcaption\">\n    The <strong>Insomnia<\/strong> checkbox keeps app awake.<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"the-idle-detection-api\"><span class=\"ez-toc-section\" id=\"The-Idle-Detection-API\"><\/span>The Idle Detection API <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>At times, even if you stare at the screen for hours,<br \/>\nit&#8217;s just useless and you can&#8217;t come up with the slightest idea what to do with your greeting card.<br \/>\nThe Idle Detection API allows the app to detect user idle time.<br \/>\nIf the user is idle for too long, the app resets to the initial state<br \/>\nand clears the canvas.<br \/>\nThis API is currently gated behind the<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Notification\/requestPermission\">notifications permission<\/a>,<br \/>\nsince a lot of production use cases of idle detection are notifications-related,<br \/>\nfor example, to only send a notification to a device the user is currently actively using.<\/p>\n<p>After making sure that the notifications permission is granted, I then instantiate the<br \/>\nidle detector.<br \/>\nI register an event listener that listens for idle changes, which includes the user and<br \/>\nthe screen state.<br \/>\nThe user can be active or idle,<br \/>\nand the screen can be unlocked or locked.<br \/>\nIf the user is idle, the canvas clears.<br \/>\nI give the idle detector a threshold of 60 seconds.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> idleDetector <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">IdleDetector<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>idleDetector<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'change'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">const<\/span> userState <span class=\"token operator\">=<\/span> idleDetector<span class=\"token punctuation\">.<\/span>userState<span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">const<\/span> screenState <span class=\"token operator\">=<\/span> idleDetector<span class=\"token punctuation\">.<\/span>screenState<span class=\"token punctuation\">;<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">log<\/span><span class=\"token punctuation\">(<\/span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`<\/span><span class=\"token string\">Idle change: <\/span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${<\/span>userState<span class=\"token interpolation-punctuation punctuation\">}<\/span><\/span><span class=\"token string\">, <\/span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${<\/span>screenState<span class=\"token interpolation-punctuation punctuation\">}<\/span><\/span><span class=\"token string\">.<\/span><span class=\"token template-punctuation string\">`<\/span><\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>userState <span class=\"token operator\">===<\/span> <span class=\"token string\">'idle'<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token function\">clearCanvas<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><p><span class=\"token keyword\">await<\/span> idleDetector<span class=\"token punctuation\">.<\/span><span class=\"token function\">start<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><br>threshold<span class=\"token operator\">:<\/span> <span class=\"token number\">60000<\/span><span class=\"token punctuation\">,<\/span><br>signal<span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><\/p><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>And as always, I only load this code when the browser supports it.<\/p>\n<p><web-copy-code><\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'IdleDetector'<\/span> <span class=\"token keyword\">in<\/span> window<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br><span class=\"token keyword\">import<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.\/idle_detection.mjs'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p><\/web-copy-code><\/p>\n<p>In the Fugu Greetings app, the canvas clears when the <strong>Ephemeral<\/strong> checkbox is<br \/>\nchecked and the user is idle for for too long.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005dbb930b1d3f8f5d1da-7386592' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005DBB930B1D3F8F5D1DA-7386592.png' alt=\"Fugu Greetings app with a cleared canvas after the user has been idle for too long.\"><figcaption class=\"w-figcaption\">\n    When the <strong>Ephemeral<\/strong> checkbox is checked and the user has been idle for too long, the canvas is cleared.<br \/>\n  <\/figcaption><\/figure>\n<h2 id=\"closing\"><span class=\"ez-toc-section\" id=\"Closing\"><\/span>Closing <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Phew, what a ride. So many APIs in just one sample app.<br \/>\nAnd, remember, I never make the user pay the download cost<br \/>\nfor a feature that their browser doesn&#8217;t support.<br \/>\nBy using progressive enhancement, I make sure only the relevant code gets loaded.<br \/>\nAnd since with HTTP\/2, requests are cheap, this pattern should work well for a lot of<br \/>\napplications,<br \/>\nalthough you might want to consider a bundler for really large apps.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000006e8d776365d6b538c9a-8214623' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000006E8D776365D6B538C9A-8214623.png' alt=\"Chrome DevTools Network panel showing only requests for files with code that the current browser supports.\"><figcaption class=\"w-figcaption\">\n    Chrome DevTools Network tab showing only requests for files with code that the current browser supports.<br \/>\n  <\/figcaption><\/figure>\n<p>The app may look a little different on each browser since not all platforms support all features,<br \/>\nbut the core functionality is always there\u2014progressively enhanced according to the particular browser&#8217;s capabilities.<br \/>\nNote that these capabilities may change even in one and the same browser,<br \/>\ndepending on whether the app is running as an installed app or in a browser tab.<\/p>\n<figure class=\"w-figure\">\n  <img alt='android-2817055' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/android-2817055.png' alt=\"Fugu Greetings running on Android Chrome, showing many available features.\" width=\"500\"><figcaption class=\"w-figcaption\">\n    <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/tomayac\/fugu-greetings\">Fugu Greetings<\/a> running on Android Chrome.<br \/>\n  <\/figcaption><\/figure>\n<figure class=\"w-figure\">\n  <img alt='safari-1435212' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/safari-1435212.png' alt=\"Fugu Greetings running on desktop Safari, showing fewer available features.\" width=\"500\"><figcaption class=\"w-figcaption\">\n    <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/tomayac\/fugu-greetings\">Fugu Greetings<\/a> running on desktop Safari.<br \/>\n  <\/figcaption><\/figure>\n<figure class=\"w-figure\">\n  <img alt='chrome-9897882' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/chrome-9897882.png' alt=\"Fugu Greetings running on desktop Chrome, showing many available features.\" width=\"500\"><figcaption class=\"w-figcaption\">\n    <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/tomayac\/fugu-greetings\">Fugu Greetings<\/a> running on desktop Chrome.<br \/>\n  <\/figcaption><\/figure>\n<p>If you&#8217;re interested in the <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/tomayac.github.io\/fugu-greetings\/public\/\">Fugu Greetings<\/a> app,<br \/>\ngo find and <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/tomayac\/fugu-greetings\">fork it on GitHub<\/a>.<\/p>\n<figure class=\"w-figure\">\n  <img alt='10000201000009c4000005fb1ef077cdf01b8588-2947610' class=\"w-screenshot\" src='https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/10000201000009C4000005FB1EF077CDF01B8588-2947610.png' alt=\"Fugu Greetings repo on GitHub.\"><figcaption class=\"w-figcaption\">\n    <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/tomayac\/fugu-greetings\">Fugu Greetings<\/a> app on GitHub.<br \/>\n  <\/figcaption><\/figure>\n<p>The Chromium team is working hard on making the grass greener when it comes to advanced Fugu APIs.<br \/>\nBy applying progressive enhancement in the development of my app,<br \/>\nI make sure that everybody gets a good, solid baseline experience,<br \/>\nbut that people using browsers that support more Web platform APIs get an even better experience.<br \/>\nI&#8217;m looking forward to seeing what you do with progressive enhancement in your apps.<\/p>\n<h2 id=\"acknowledgements\"><span class=\"ez-toc-section\" id=\"Acknowledgements\"><\/span>Acknowledgements <span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I&#8217;m grateful to <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/christianliebel.com\/\">Christian Liebel<\/a> and<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/h3manth.com\/\">Hemanth HM<\/a> who both have contributed to Fugu Greetings.<br \/>\nThis article was reviewed by <a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/jpmedley\">Joe Medley<\/a> and<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/kaycebasques\">Kayce Basques<\/a>.<br \/>\n<a rel=\"nofollow noopener noreferrer\" target=\"_blank\" href=\"https:\/\/github.com\/jakearchibald\/\">Jake Archibald<\/a> helped me find out the situation<br \/>\nwith dynamic <code>import()<\/code> in a service worker context.<\/p>\n<p>    <web-feedback additional-questions=\"\"><\/web-feedback>\n  <\/div>\n<p><script async src=\"\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Con\u00e7u pour les navigateurs modernes et am\u00e9lior\u00e9 progressivement comme en 2003 29 juin 2020 Mis \u00e0 jour le 10 juillet 2020 Appara\u00eet dans : Progressive Web Apps En mars 2003, Nick Finck et Steve\u2026<\/p>","protected":false},"author":1,"featured_media":83085,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1356],"tags":[],"class_list":["post-83086","post","type-post","status-publish","format-standard","has-post-thumbnail","category-dev"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.0 (Yoast SEO v28.2) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Progressively enhance your Progressive Web App - R Marketing Digital<\/title>\n<meta name=\"description\" content=\"Building for modern browsers and progressively enhancing like it&#039;s 2003 Jun 29, 2020 Updated Jul 10, 2020 Appears in: Progressive Web Apps Back in March\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/rmarketingdigital.com\/fr\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Progressively enhance your Progressive Web App\" \/>\n<meta property=\"og:description\" content=\"Building for modern browsers and progressively enhancing like it&#039;s 2003 Jun 29, 2020 Updated Jul 10, 2020 Appears in: Progressive Web Apps Back in March\" \/>\n<meta property=\"og:url\" content=\"https:\/\/rmarketingdigital.com\/fr\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/\" \/>\n<meta property=\"og:site_name\" content=\"R Marketing Digital\" \/>\n<meta property=\"article:published_time\" content=\"2022-09-25T20:03:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/thumbnail-4237234.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"752\" \/>\n\t<meta property=\"og:image:height\" content=\"480\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"R Marketing Digital\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/\"},\"author\":{\"name\":\"R Marketing Digital\",\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/#\\\/schema\\\/person\\\/7c0d9782b2977b840b3740f00c69a73e\"},\"headline\":\"Progressively enhance your Progressive Web App\",\"datePublished\":\"2022-09-25T20:03:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/\"},\"wordCount\":3504,\"image\":{\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/rmarketingdigital.com\\\/wp-content\\\/uploads\\\/2020\\\/09\\\/thumbnail-4237234.jpg\",\"articleSection\":[\"Dev\"],\"inLanguage\":\"fr-FR\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/\",\"url\":\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/\",\"name\":\"Progressively enhance your Progressive Web App - R Marketing Digital\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/rmarketingdigital.com\\\/wp-content\\\/uploads\\\/2020\\\/09\\\/thumbnail-4237234.jpg\",\"datePublished\":\"2022-09-25T20:03:10+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/#\\\/schema\\\/person\\\/7c0d9782b2977b840b3740f00c69a73e\"},\"description\":\"Building for modern browsers and progressively enhancing like it's 2003 Jun 29, 2020 Updated Jul 10, 2020 Appears in: Progressive Web Apps Back in March\",\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/de\\\/dev\\\/verbessern-sie-ihre-progressive-web-app-schrittweise\\\/#primaryimage\",\"url\":\"https:\\\/\\\/rmarketingdigital.com\\\/wp-content\\\/uploads\\\/2020\\\/09\\\/thumbnail-4237234.jpg\",\"contentUrl\":\"https:\\\/\\\/rmarketingdigital.com\\\/wp-content\\\/uploads\\\/2020\\\/09\\\/thumbnail-4237234.jpg\",\"width\":752,\"height\":480,\"caption\":\"thumbnail-4237234-4592704-jpg\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/#website\",\"url\":\"https:\\\/\\\/rmarketingdigital.com\\\/\",\"name\":\"R Marketing Digital\",\"description\":\"Agencia SEO | Publicidad redes sociales | Community Management\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/rmarketingdigital.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/rmarketingdigital.com\\\/#\\\/schema\\\/person\\\/7c0d9782b2977b840b3740f00c69a73e\",\"name\":\"R Marketing Digital\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/c5599c203e0120b8ccbff4666af86f35f9b3dcad468a8bc6295313a3964a80be?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/c5599c203e0120b8ccbff4666af86f35f9b3dcad468a8bc6295313a3964a80be?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/c5599c203e0120b8ccbff4666af86f35f9b3dcad468a8bc6295313a3964a80be?s=96&d=mm&r=g\",\"caption\":\"R Marketing Digital\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Progressively enhance your Progressive Web App - R Marketing Digital","description":"Building for modern browsers and progressively enhancing like it's 2003 Jun 29, 2020 Updated Jul 10, 2020 Appears in: Progressive Web Apps Back in March","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/rmarketingdigital.com\/fr\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/","og_locale":"fr_FR","og_type":"article","og_title":"Progressively enhance your Progressive Web App","og_description":"Building for modern browsers and progressively enhancing like it's 2003 Jun 29, 2020 Updated Jul 10, 2020 Appears in: Progressive Web Apps Back in March","og_url":"https:\/\/rmarketingdigital.com\/fr\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/","og_site_name":"R Marketing Digital","article_published_time":"2022-09-25T20:03:10+00:00","og_image":[{"width":752,"height":480,"url":"https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/thumbnail-4237234.jpg","type":"image\/jpeg"}],"author":"R Marketing Digital","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/#article","isPartOf":{"@id":"https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/"},"author":{"name":"R Marketing Digital","@id":"https:\/\/rmarketingdigital.com\/#\/schema\/person\/7c0d9782b2977b840b3740f00c69a73e"},"headline":"Progressively enhance your Progressive Web App","datePublished":"2022-09-25T20:03:10+00:00","mainEntityOfPage":{"@id":"https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/"},"wordCount":3504,"image":{"@id":"https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/#primaryimage"},"thumbnailUrl":"https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/thumbnail-4237234.jpg","articleSection":["Dev"],"inLanguage":"fr-FR"},{"@type":"WebPage","@id":"https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/","url":"https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/","name":"Progressively enhance your Progressive Web App - R Marketing Digital","isPartOf":{"@id":"https:\/\/rmarketingdigital.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/#primaryimage"},"image":{"@id":"https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/#primaryimage"},"thumbnailUrl":"https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/thumbnail-4237234.jpg","datePublished":"2022-09-25T20:03:10+00:00","author":{"@id":"https:\/\/rmarketingdigital.com\/#\/schema\/person\/7c0d9782b2977b840b3740f00c69a73e"},"description":"Building for modern browsers and progressively enhancing like it's 2003 Jun 29, 2020 Updated Jul 10, 2020 Appears in: Progressive Web Apps Back in March","inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/rmarketingdigital.com\/de\/dev\/verbessern-sie-ihre-progressive-web-app-schrittweise\/#primaryimage","url":"https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/thumbnail-4237234.jpg","contentUrl":"https:\/\/rmarketingdigital.com\/wp-content\/uploads\/2020\/09\/thumbnail-4237234.jpg","width":752,"height":480,"caption":"thumbnail-4237234-4592704-jpg"},{"@type":"WebSite","@id":"https:\/\/rmarketingdigital.com\/#website","url":"https:\/\/rmarketingdigital.com\/","name":"Marketing num\u00e9rique","description":"Agence de r\u00e9f\u00e9rencement | Publicit\u00e9 sur les r\u00e9seaux sociaux | Gestion de communaut\u00e9","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/rmarketingdigital.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Person","@id":"https:\/\/rmarketingdigital.com\/#\/schema\/person\/7c0d9782b2977b840b3740f00c69a73e","name":"Marketing num\u00e9rique","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/secure.gravatar.com\/avatar\/c5599c203e0120b8ccbff4666af86f35f9b3dcad468a8bc6295313a3964a80be?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/c5599c203e0120b8ccbff4666af86f35f9b3dcad468a8bc6295313a3964a80be?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/c5599c203e0120b8ccbff4666af86f35f9b3dcad468a8bc6295313a3964a80be?s=96&d=mm&r=g","caption":"R Marketing Digital"}}]}},"_links":{"self":[{"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/posts\/83086","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/comments?post=83086"}],"version-history":[{"count":0,"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/posts\/83086\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/media\/83085"}],"wp:attachment":[{"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/media?parent=83086"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/categories?post=83086"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rmarketingdigital.com\/fr\/wp-json\/wp\/v2\/tags?post=83086"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}