What’s new on iOS 12.2 for Progressive Web Apps

One year after the first initial support for PWAs on iOS, Apple released iOS 12.2 for iPhone and iPads with what it seems to be the biggest step forward in the last year, addressing the two most annoying problems we’ve been dealing with PWAs: reload effect and OAuth logins.

However, as expected, the release is not free of bugs or problems, but at least several problems I’ve reported from the first beta seems solved.

In a nutshell

I will get deeper on most topics, but if you don’t have time to read:

  • 💾 PWAs have a new lifecycle and on most normal situations, the state of your app will be saved and restored between sessions, but there is no way to kill the PWA and restart it from scratch
  • 🔁 A link to an external site opens in a “PWA In-App browser” instead of Safari and if the external site links to the PWA’s scope again, the navigation goes back to the PWA. Now we can log in users on external origins within a PWA (such as with OAuth).
  • 🔙 We have navigation gestures to go back and forward using “swipe from edges” within PWAs and the In-App Browser.
  • ⚠️ ️️The new In-App Browser means there is no way to call Safari from a PWA
  • 🤝 Web Share is supported (over HTTPS only)
  • ⚠️ Motion Sensors events are disabled by default and there is no API yet to request access to them
  • 🚨 HTTP websites will be marked as “Not Secure” in the URL bar
  • ⚠️ Old getUserMedia API was removed in favor of WebRTC, so if you are using the camera in Safari you might want to update your code.
  • 😳 PWAs in standalone mode are not inspectable anymore with desktop Safari (the Web App process doesn’t appear as inspectable)
  • 👍 IntersectionObserver, Conic Gradients CSS, <datalist>, <input type=color>, Abortable Fetchs and inputmode attribute are now available
  • 👎 Same bugs and disabled features as in previous versions: no access to Camera within a standalone PWA, no full support of Web App Manifest, no installation API, ghost windows, and no default launch image.
“Not secure”: the new flag when you browse HTTP websites

The new Lifecycle

One of the worst issues for PWAs on iOS until now -if not the worst- is the reload problem. Every time you get out of a PWA, the instance is terminated, so if you go back to it, it restarts from scratch. That was making many PWAs unusable on iPhones and iPads, such as Starbucks, Uber and even Twitter in some situations, such as when using two-factor authentication.

Apple seemed to take note of the issue and in this release: there is a new lifecycle. While it’s not completely clear how it works, based on a couple of hours of testing this is my conclusion.

New PWA Lifecycle on iOS 12.2 according to my testing. focus, blur and visibility events only happen when loaded over HTTPS.

Detecting state change

While iOS is not supporting yet the new Page Lifecycle API (that will be really useful here), we can take advantage of the Page Visibility events: focus, blur, and visibilitychange. You can do that, for example, to store the app’s state in local storage to retrieve it later in the case of a full reload.

IMPORTANT: Events are only fired if the page is loaded through HTTPS. For some reason, they are not fired on HTTP.

When does the Web context run?

Your PWA will be executed if it’s in the foreground with no PWA Browser actually running. If the app is minimized (home screen button or gesture, or opening other app), you will have approximately 5 seconds in the background to execute code and, after that, your app is frozen.

When your PWA opens an external site with the new In-App Browser with target=_blank, you have approximately 2 seconds in the background before frozen. Then your web page context might resume when the browser is closed if the page is not reloaded.

The process

  • When you get out of a PWA, the “Web.app” or the mirror universe counterpart “WebApp1.app” processes are terminated immediately, but the web context is frozen and saved somewhere (that is, your JavaScript context, browsing context, scroll position, zoom state, form data). No more JavaScript execution after that happens; also no video will play in the background; audio is playing fine in the background.
  • Every PWA icon (a WebClip Bundle) has its own saved context, so two icons from the same PWA won’t share their context (but they share storage). If you open one and immediately another one, the first one will use Web and the second one WebApp1.
  • If you close the screen of your PWA from the multitask mode (killing it), it doesn’t delete the saved state! So if you open the PWA again from the icon, it will continue as its previous state. That means there is no way to reload the app or start it from scratch from the beginning from a user action.
  • If you go back to the app using multitask, a gesture or tapping on the home screen’s icon again, the PWA engine process starts from scratch but quickly restores your PWA state if available. So it’s not completely available as soon you go back as a native, but it’s much faster than starting it from scratch. On an iPhone X, there is approximately a one-second context load delay (white screen or launch image) when you go back to the PWA.
  • After some undefined time, the saved context seems to disappear. So if you get out of the PWA, do nothing with your phone and wait some hours to go back to the PWA, it restarts from scratch.
  • While the Web and Web1 processes seem to disappear from memory when you get out of the app, the saved context is shared with the rest of the OS memory. That means, if you get out of the PWA and you open a high-memory consuming native app, the saved contexts for all the PWAs might disappear and when you go back to the PWA, even after 20 seconds, your PWA might start from scratch as in iOS 12.1, as with native apps.
  • When you got out of the app, the OS still shows the launch image instead of a screenshot of the last moment you used the app. And if you don’t provide a launch image -as most PWAs unfortunately- you just see a white screen.

PWA Browser

Up to iOS 12.1, every link pointing to a destination outside of the manifest’s scope opens in Safari by default. From iOS 12.2, an In-App PWA Browser is used. Therefore, all your links now open within your own app’s scope and they may have a “Done” button to go back to your app.

Every navigation that you make on that browser will happen there. Back gesture is enabled (dragging from the edge to the right) and if you are in the top level of that browser, it goes back to your PWA’s window.

At the left, what happens today in iOS 12.1, the external URL opens in Safari with a small “back” button to your PWA in the status bar. At the right, two images on how it works on iOS 12.2. The URL opens in an In-App browser within the visual scope of your PWA

Sharing data with Safari

Websites loaded in this browser are not sharing storage or service workers with Safari. They share it with other instances of PWA Browsers though. So, if you are logged in on Facebook within Safari, you won’t be logged in on a PWA using OAuth.

The Done button to return to the PWA only appears when you load another page after initial external site load.

Detecting PWA Browser

If you have a website and you want to know if it’s being loaded in a PWA In-App Browser, you can use today: navigator.standalone or the display-mode: standalone media query.

While it seems good, it’s actually bad news. Let’s say you don’t have a PWA. That means, you never expect your display mode to be standalone. Well, now it will be exposed in that mode when it’s loaded from a PWA. Weird.

HTTP Headers including User Agent are the same as in Safari.

Going back to your app

When the external URL redirects or points to a URL within the scope -including POST requests, JavaScript redirects or links-, the PWA closes the browser and loads the content in the standalone window, coming back to the original PWA. This is particularly useful for OAuth authentication. The In-App browser shares storage context with the opener PWA, so you can use URLs outside of the scope but within the same origin to share data as well.

Something similar happens when you press the “Done” button (if it exists) or if you navigate back with the gesture. The PWA Browser closes and you return to the PWA.

Playing with the PWA lifecycle

Opening external links with or without target=_blank make a difference as in:

  • Without target=_blank, the PWA context is deleted, so when the browser closes, the page reloads in the standalone window
  • With target=_blank, the PWA context is restored when the browser closes

Opening links using Safari is impossible

I couldn’t find a way to let a PWA open a link in Safari though. I’ve tried everything I had in my recipe list: playing with target, with rel, with JavaScript, using Custom URIs. Nothing works; every link will open in your own app.

You can only point to Chrome or Firefox (if the user have them) using the googlechrome(s):// or firefox:// URI schemes.

If you install the appsco.pe PWA you can launch suggested PWAs within its boundaries but you can’t install them or transfer the navigation to Safari

For example, we can see a problem with this impossibility on PWAs such as AppScope, where you can launch other PWAs. While you can run them in the InApp Browser, there is no way you can add them to the home screen or switch into Safari to do that.

The PWA Browser is nice but…

While I think this solution is a step forward, there are some issues to solve:

  • There should be a way to request Safari for some cases (maybe with an specific rel or target attribute)
  • The Done button doesn’t appear at the first glance, so the user will be confused about how to cancel the popup browser (the back gesture is obscure)
  • No UI customization, such as using the theme-color
  • No way to see the full URL, only the domain
  • No visual way to navigate: back button, reload, etc.
  • No way to check TLS/SSL certificate to avoid phishing attacks
  • No way to move the navigation to Safari (as in Safari View Controller -the one used by the native Twitter app)
  • No way to share the URL from the browser
  • Standalone media queries and JS standalone attribute are exposed as true when it doesn’t seem the case

Motion Sensors Disabled

Because of some privacy issues, Safari has decided to disable Motion Sensors (accelerometer and gyroscope) access by default without an alternative API. This is used right now by AR, VR and 360 web apps to render content as well as games and other use cases.

The only solution right now is to show a message to the user to go to Settings and enable the feature until some of the events are fired once (such as deviceorientation).

There is an API coming to request permission per site, but the WebKit team didn’t have time to get it done for this release. Chris Dumez from Apple confirmed that.

Navigation Gestures

Back gesture in action in a standalone PWA

Progressive Web Apps now have the navigation gestures enabled, similar to Safari. It works for every URL within your scope, including client-side routes and it’s mostly useful for the back action as there is no back button on the screen while in standalone mode. It also works when the new In App Browser is used (for navigation within external URLs and to go back to the internal window if you are at the top level).

Other changes

Web Share

Web Share Level 1 is now supported on all the Web platforms, including Safari, standalone PWA, WebView (such as Facebook Mobile Browser or Chrome on iOS) and Safari View Controller (such as a link opened within the Twitter native app). Only the text and URL attributes are working; the title is ignored.

Web Share works only after a user gesture and lets you share a link with a text on any native app, including native behaviors such as AirDrop or Copy.

Form additions

The first addition is the new color picker using the same space as the keyboard

With <input type=color> we can render a color picker. There is a bug here: on landscape mode the last row of colors is unselectable

The second addition is support for <datalist> that looks very similar to a <select> but only when clicking on the little arrow at the right. The arrow appears when there are suggestions available, such as when the input is empty or when you start typing and there are options with that substring. The suggested options also appear on the keyboard for fast selection.

With datalist, we can have an open input text box with suggestions on the keyboard and within a dropdown menu

And the third change is the support for inputmode, an attribute that can be applied to <input>, <textarea> and any element with contenteditable, so now you can specify different keyboards for those cases.

Other Problems

On the bad side, standalone PWAs (known as WebClip bundles internally on iOS) are still powered by the weird Web.app process and its sibling WebApp1.app. That means it’s not 100% Safari or SFViewController so it has its own bugs and issues. For example, this means PWAs can’t still make use of WebRTC or the camera stream as there is a bug that has been there for years now. Therefore PWAs that need to open the camera, such as QR Code readers can only take snapshots.

PWAs on iOS 12.2 are still working under “Web” and “WebApp1” and not under Safari or SFViewController. The technical reason for this behavior is for another article :)

Web App Manifest support

Support for the Web App Manifest spec is still poor and undocumented. The implementation doesn’t make any use of the manifest for:

  • Icons — I actually agree with Apple on this as most icons are not iOS-optimized, but the latest version of the spec has a solution for that.
  • Orientation or display: fullscreen
  • Splash screens
  • Theme-color and background-color
  • Events: appinstalled or beforeinstallprompt (the one that let us create a custom installation dialog trigger) are not triggered
Apple is still lying to us saying that the Web App Manifest is “In Development” and not “Partially implemented” as it actually is

Bugs

I’ve seen many bugs that I could not replicate again later, such as problems with custom keyboards (I needed to restart the device to solve that); problems with launch images (it doesn’t load or it uses other PWA’s image) or that one PWA icon was using the iOS 12.1 engine (so no navigation gestures, no In-App Browser, etc.)

Weird keyboard problems using custom keyboards, such as: not updating layout after an orientation change or seeing no keys at all.
My PWA (without any launch image) using the Air Horner image for some reason

Also, well known bugs are still there, such as the ghost apps after you delete PWAs from the Home Screen.

Ghost windows within the iOS multitask switcher

Still waiting for

Many of the expected APIs and behavior compared with other browsers are still not supported, such as:

  • Web Push
  • Background Sync
  • Page Lifecycle
  • Service Workers on WebViews (so no PWAs on Chrome or Facebook)
  • Universal Links / Link Capturing