How to reload a Create React App that's been Added to iOS Home Screen

I’ve built and deployed a React app using Create React App to Netlify. It works great!

I then added my site to my iPhone’s home screen with the “Add to Home Screen” button, which sort of turns it into a PWA. However, I did disable my React app’s service worker.

I’m having issues with caching and reloading the app.

If I push new code to Netlify and it re-deploys, I can see it if I visit my site in the browser, but the homescreen app is stale. It doesn’t get an update, even if I close the “app” and reopen it.

I’ve googled this and folks have mentioned adding some sort of task that polls the server looking for a new version of my app, and then prompting the user to refresh the app which would trigger a hard reload, using something like window.location.reload(true). I wanted to ask here what’s the “best practice” here? It seems like there’s a lot of friction to get this working right. Would it be possible to solve by changing the cache headers Netlify uses for my app’s index.html file? (All the assets are fingerprinted so I know HTTP caching is not the issue there.)

If I do have to implement this myself, what’s a common way to “check the server” for a new version of my app?

Would love to hear how folks are solving this!

I hacked together a solution but it took me quite a long time! I’m still curious if there’s a better/more idiosyncratic way to achieve this.

First, I added one of Netlify’s environment variables to my React app by first creating a .env file and adding this:

# .env
REACT_APP_COMMIT_REF=$COMMIT_REF

I decided to use COMMIT_REF as a proxy for the version number (because all I care about is whether a new version has been deployed to the server).

Then I put this ENV var in my public/index.html file. (Here’s where I would have loved to be able to put it into a new public/VERSION.txt file. Is is possible to generate new files at build-time with Netlify?)

Finally, I added some code to my React app that fetches the index.html from the server, parses it, and reads that meta tag to see if a new version is available.

// index.js
let currentVersion = process.env.REACT_APP_COMMIT_REF;

if (process.env.NODE_ENV === "production") {
  document.addEventListener("visibilitychange", () => {
    if (document.visibilityState === "visible") {
      fetch(`/index.html?_=${Date.now()}`)
        .then(res => res.text())
        .then(htmlString => {
          let doc = new DOMParser().parseFromString(htmlString, "text/html");
          let latestVersion = doc
            .querySelector("meta[name='app-version']")
            .getAttribute("content");

          if (latestVersion !== currentVersion) {
            window.location.reload(true);
          }
        });
    }
  });
}

Right now, the code just runs on visibilitychange, so if I multitask away from my app and then reopen it, this code will run. That way when I come back and open the app, if there’s a new version the app reloads and I get the latest code!

Would love to hear any comments or feedback on this.

Thanks for this! Here’s a simple way to use a version.txt like you were wanting:

fetch(`/version.txt?_=${Date.now()}`)
    .then(res => res.text())
    .then(latestVersion => {
 
      if (latestVersion !== currentVersion) {
        window.location.reload(true);
      }
    });

Change your build step, in my case, react-scripts build becomes REACT_APP_COMMIT_REF=$COMMIT_REF react-scripts-build && echo $COMMIT_REF >> build/version.txt

1 Like

Hi, @zackify, this is fantastic and thank you for sharing this. :smiley: