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.
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.
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.
- 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.
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.
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.
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
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
You can only point to Chrome or Firefox (if the user have them) using the googlechrome(s):// or firefox:// URI schemes.
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.
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).
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.
The first addition is the new color picker using the same space as the keyboard
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.
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.
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.
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
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.)
Also, well known bugs are still there, such as the ghost apps after you delete PWAs from the Home Screen.
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