title: ReactJs PWA not updating on iOS
body: <p>I'm building a ReactJs PWA but I'm having trouble detecting updates on iOS.</p>
  <p>On Android everything is working great so I'm wondering if all of this is related to iOS support for PWAs or if my implementation of the service worker is not good.</p>
  <p>Here's what I've done so far:</p>
  <p><strong>Build process and hosting</strong></p>
  <p>My app is built using webpack and hosted on AWS. Most of the files (js/css) are built with some hash in their name, generated from their content. For those which aren't (app manifest, index.html, sw.js), I made sure that AWS serves them with some Cache-Control headers preventing any cache. Everything is served over https.</p>
  <p><strong>Service Worker</strong></p>
  <p>I kept this one as simple as possible : I didn't add any cache rules except precache for my app-shell:</p>
  <pre class="lang-js prettyprint-override"><code>workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
  </code></pre>
  <p><strong>Service-worker registration</strong></p>
  <p>Registration of the service worker occurs in the main ReactJs App component, in the componentDidMount() lifecycle hook:</p>
  <pre class="lang-js prettyprint-override"><code>componentDidMount() {
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/sw.js')
        .then((reg) =&gt; {
          reg.onupdatefound = () =&gt; {
            this.newWorker = reg.installing;
            this.newWorker.onstatechange = () =&gt; {
              if (this.newWorker.state === 'installed') {
                if (reg.active) {
                  // a version of the SW is already up and running
  
                  /*
                    code omitted: displays a snackbar to the user to manually trigger
                    activation of the new SW. This will be done by calling skipWaiting()
                    then reloading the page
                  */
                } else {
                  // first service worker registration, do nothing
                }
              }
            };
          };
        });
    }
  }
  </code></pre>
  <p><strong>Service worker lifecycle management</strong></p>
  <p>According to the <a href="https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#updates" rel="noreferrer">Google documentation about service workers</a>, a new version of the service worker should be detected when navigating to an in-scope page. But as a single-page application, there is no hard navigation happening once the app has been loaded.</p>
  <p>The workaround I found for this is to hook into react-router and listen for route changes, then manually ask the registered service worker to update itself :</p>
  <pre class="lang-js prettyprint-override"><code>const history = createBrowserHistory(); // from 'history' node package
  history.listen(() =&gt; {
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker
        .getRegistration()
        .then((reg) =&gt; {
  
          if (!reg) {
            return null;
          }
  
          reg.update();
        });
    }
  });
  
  </code></pre>
  <h2>Actual behavior</h2>
  <p>Throwing a bunch of <code>alert()</code> everywhere in the code showed above, this is what I observe :</p>
  <ol>
  <li>When opening the pwa for the first time after adding it to the homescreen, the service worker is registered as expected, on Android and iOS</li>
  <li>While keeping the app opened, I deploy a new version on AWS. Navigating in the app triggers the manual update thanks to my history listener. The new version is found, installed in the background. Then my snackbar is displayed and I can trigger the switch to the new SW.</li>
  <li>Now I close the app and deploy a new version on AWS. When opening the app again :
  <ul>
  <li>On Android the update is found immediately as Android reloads the page</li>
  <li>iOS does not, so I need to navigate within the app for my history listener to trigger the search for an update. When doing so, the update is found</li>
  <li>After this, for both OS, my snackbar is displayed and I can trigger the switch to the new SW</li>
  </ul>
  </li>
  <li>Now I close the app and <em>turn off</em> the phones. After deploying a new version, I start them again and open the app :
  <ul>
  <li>On Android, just like before, the page is reloaded which detects the update, then the snackbar is displayed, etc..</li>
  <li>On iOS, I navigate within the app and my listener triggers the search for an update. <strong>But this time, the new version is never found and my <code>onupdatefound</code> event handler is never triggered</strong></li>
  </ul>
  </li>
  </ol>
  <hr />
  <p>Reading <a href="https://medium.com/@firt/whats-new-on-ios-12-2-for-progressive-web-apps-75c348f8e945" rel="noreferrer">this post on Medium from Maximiliano Firtman</a>, it seems that iOS 12.2 has brought a new lifecycle for PWAs. According to him, when the app stays idle for a long time or during a reboot of the device, the app state is killed, as well as the page.</p>
  <p>I'm wondering if this could be the root cause of my problem here, but I was not able to find anyone having the same trouble so far.</p>