Getting started with PWAs: an iOS nightmare

5 issues and how to fix them

Marie Foussette
7 min readNov 15, 2018
Image credit: https://responsivedesign.is/articles/altering-expectations-by-improving-pwa-on-ios/

Progressive Web Apps are great…

… or at least everyone says so. I cannot count the number of articles I read promoting PWAs, saying it’s the future of apps (web and mobile). I even passed an interview for a big company that believes in PWA so much that they’re developing their new product around it.

I get why they want to push them forward, it’s:

  • easier to create (you don’t need iOS and Android developers, just web developers)
  • easier to deploy (no need to deploy on the App Store and Google Play Store, just deploy on the web and prompt the user to add your web app to the home screen)

Theory is nice but practice is better. So let’s put PWA to the test by trying to build one using Vue.js.

Coding & Debugging: Home sweet home

For a web developers, PWAs development is godsent. No need for a new environment, no need to learn a new language, you already got everything, you just need a bit of config to do. And if you’re using Vue.js like I did, just use the Vue CLI and select the PWA template option and you’re set, just like that.

You can use Chrome like you always do. You can connect an Android device and debug it from your computer. You can even do the same with your iPhone and Safari.

I’d recommend using Lighthouse to run audits on your app to make sure you’re going in the right direction in terms of PWA best practices and to read the PWA Checklist by Google.

You’ll be able to create your app in no time. Then you’ll install it on your mobile device. And that’s where you’re going to hit walls. And you’re going to hit some pretty hard.

The good, the bad and the ugly fix

#1 Installation prompt

Nothing to do there on Android. If you go to a PWA using Chrome, you’ll get a message prompting you to add the app on your home screen.

Prompt to add to home screen on Android

On iOS… Nothing. You need to build your own message to tell the user how to add your app to their home screen (by tapping on Share -> Add to homescreen). That’s not much work, but you’ll have to remember to display this message only for iOS users. And that’s just the beginning.

#2 Splash screens

On Android, no issue there. It’ll take every info it needs from your manifest.json to generate a splash screen for you using your icons, theme_color and background_color.

On iOS however, no generation. You’ll need to get your favorite graphic editor and create a splash screen for each Apple device resolution there is. Or you can use an app like ApeTools.

After that, you’ll have a bit of config to do.

First copy the following code, paste it in the head part of your index.html and change the href attribute for each link with your own values.

Now for your splash screen to show up, you’ll need to add the following line in the head of your index.html file.

<meta name="apple-mobile-web-app-capable" content="yes">

If you used VueCLI3 to generate your PWA, Vue is going to overwrite this property on build, you’ll need to tweak your config a bit. If you don’t have a vue.config.js at the root of your project, then create it. It should look like this:

module.exports = {  
pwa: {
appleMobileWebAppCapable: 'yes'
}
};

Now your app will display a splash screen on both Android and iOS, instead of a blend white screen.

Splash screen on iOS

#3 Persistent state: the goldfish theory

If you added a standalone PWA on your Android, you can use it, switch to another app and come back to it without any issue.

On iOS though… If the app is closed (even if it’s kept in the background), it’s going to lose its state. If you open your PWA, switch to your music app and come back, you’ll be brought back to your root screen since the PWA will have restarted. Everything is lost and you’ll need to remind your app its own state each time it restarts.

It was a nightmare to debug as Safari’s console would disappear each time the app is not in focus, but I finally got a working hack after hours of browsing the web hoping for an answer.

vuex-router-sync and vuex-persist to the rescue!

First, add vuex-router-sync and vuex-persist to your project dependencies.

>_ yarn add vuex-router-sync && yarn add vuex-persist

Then, we’ll configure vuex-router-sync in your main.ts in order to save your route state inside your Vuex store. You should have something that looks a bit like this:

Then, we’ll edit our store.ts and configure vuex-persist to persist our app’s state in the localStorage.

Now, I used the reducer option of VuexPersistence to save only what I need. I’m saving the route state (created by vuex-router-sync) and a lastLaunch property that will allow me to choose between rerouting to the last visited page or start fresh.

Our route state object and our lastLaunch is stored inside the localStorage. Now what?

My main goal is to redirect the user to the right state when he leaves the PWA. In my manifest.json, the start_url is /. But there’s nothing on that /, I’m just forcing everyone to pass by it so I can redirect them to the right route. Here’s what it looks like:

Persist the app state: go to About, leave the app, relaunch the app, it should be on the About screen

It’s clearly not the cleanest hack but it works. You could also create an AuthGuard to do the same logic.

#4 WakeLock

WakeLock is what allows a device to stay awake (its screen won’t turn off). While this feature shouldn’t be abused to avoid draining the device’s battery, it’s still useful. Let’s say you created a workout app. You need to screen to stay on while you’re working out to see the instructions.

Well, on both Android and iOS, this feature is non-existent. There is a Wake Lock API still in development, not supported by any browser and therefore unusable for now.

After spending several hours browsing StackOverflow, the only solution I found is to play a video in the background of the app. It’s a really ugly fix and I can’t stress enough how much I hate this hack but it’s all we have right now.

There’s a library called noSleep.js that does that for you. I’ve seen people saying it works, some that it doesn’t. I got it working on an iPhone XS and a Google Pixel.

Using Typescript, first declare the NoSleep module in the definition file, then, you can use noSleep.js in your app:

Note that if you use the noSleep.js library, you won’t be able to listen to music while noSleep is enable. I made a fork to allow that, you can find it there.

#5 Navigation: the labyrinth without exit

I didn’t use Android for maybe 2 years, but as far as I know, there’s a back button provided by the system.

Android navigation bar

Well on iOS there’s no button. iOS is more of a gesture-based OS, and to go back you either swipe left or you need a specific button inside the app. Since PWA runs using the browser, you could have thought the swipe gestures will be there too. But it’s not. If you create an app with deep linking without any mean to return to your home screen, your user might end up being stuck and he’s only solution would be to kill the app.

And if you implemented the persisted state thing that I talk about earlier, well… Killing the app won’t get you anywhere since your state is saved in your localStorage and is going to be reapplied on restart. You’ll have to either erase its data in Safari Advanced settings or uninstall / reinstall the PWA.

Now, the easiest solution is to implement a back button and put it when needed, or create a bottom navigation or a header touch that will allow the user to get back to the root state. But to me, the best solution is to emulate the swipe left to go back technique since your iOS users are used to it.

To do that, you can reuse this SwipeHandlerService:

To make it work, export your router from your main.ts, import it inside the SwipeService, call Router.go(-1) where you see the swipe right comment and call the init method inside your App.vue mounted lifecycle hook.

PWA “may be” the future

I can see why web developers love PWAs. Or the concept at least. Because in reality, you see how limited they are for now. It’s still clearly a work in progress.

It’s anything but magic, mainly Chrome and Safari are not on the same page. And if, as a web developer you could afford not supporting Safari for your web app, you can’t this time. On iOS, Chrome runs using the same core as Safari, so you won’t get anywhere by inviting your users to use a different browser. And there’s just too many iOS users out there for you to ignore them completely.

You’ll then try to make your app work on two different system, with different rules and different capabilities. While it’s doable, it’s something to bear in mind while starting a PWA project. You can’t just think your app is going to have the same behavior on both platforms without you doing any work.

--

--