◀ Back to NotesPublished on May 31, 2023

Install any website as PWA on iOS

I have a few websites that I need to use often on my iPhone. I want to have them as PWA (Progressive Web Apps) so I can use them as if they were native apps.

This helps by making those sites readily available in the App Switcher, on the home screen and in Spotlight Search. It also gives me a bit more screen real estate by hiding the browser UI.

Websites can be installed as PWA if they provide a manifest in the form of:

<link rel="manifest" href="manifest.json">

This manifest file contains metadata about the app, like the name, icons, theme color, etc.

The websites I use don't provide this manifest file, so I found a way to add it myself.

I use a Safari extension called Modificator to add the manifest file to the HTML and a simple API that I cooked for generating arbitrary manifest files.

The API supports almost all the manifest properties and can be used as such:

curl 'https://api.lowtechguys.com/manif?name=Sentry'

{
    "name": "Sentry",
    "short_name": "Sentry",
    "display": "standalone",
    "orientation": "portrait"
}

# Sentry

Here I'll demonstrate adding a Sentry error reporting dashboard as an app on the home screen.

In the Modificator app, I needed to add a JS modificator as such:

document.head.innerHTML += "<link rel=manifest href=https://api.lowtechguys.com/manif?name=Sentry>"

Looks like the Modificator app doesn't properly escape single quotes so I needed to make sure I don't use them at all.

Then I went on the Sentry website, adjusted the zoom to 75% to allow more content to be visible, pressed the Share button at the bottom and tapped Add to Home Screen.


The app looks like this on the home screen:

And this is how it looks on an iPhone 12 mini:

# A more complex example

In the case of Sentry, we were helped by the fact that the site defines a good quality icon and a theme color in its head

<link rel="apple-touch-icon" href="https://s1.sentry-cdn.com/_static/c1d4f32cdd3bcf7e5d26c2cbf4f29f2f/sentry/images/logos/apple-touch-icon.png">
<meta name="theme-color" content="#2f1937">

We would need to add a few more parameters to the API call for websites that don't have those properties defined.

As an example, I'll add the FAQ website of my adaptive brightness app, Lunar. I have to reference it often when responding to support emails.

The modificator JS will be the following:

document.head.innerHTML += "<meta name=mobile-web-app-capable content=yes>" +
    "<link rel=manifest href=https://api.lowtechguys.com/manif?name=Lunar+FAQ&start_url=https%3A%2F%2Flunar.fyi%2Ffaq&icon=https%3A%2F%2Ffiles.lunar.fyi%2Flunar-icon-maskable.png>"

Here's what we're doing this time, exemplified with a curl command:

curl -G 'https://api.lowtechguys.com/manif' \
    --data-urlencode name='Lunar FAQ' \
    --data-urlencode color=#161125 \
    --data-urlencode icon=https://files.lunar.fyi/lunar-icon-maskable.png

{
    "name": "Lunar FAQ",
    "short_name": "Lunar FAQ",
    "display": "standalone",
    "orientation": "portrait",
    "icons": [
        {
            "src": "https://files.lunar.fyi/lunar-icon-maskable.png",
            "purpose": "any maskable"
        }
    ],
    "theme_color": "#161125",
    "background_color": "#161125"
}

# Showcase

# Caveats

The method doesn't work on websites with a more restrictive Content Security Policy because the manifest has to be fetched from an external host (api.lowtechguys.com in this case).

If the CSP is sent as a header, there's nothing you can do in JS to make that policy less restrictive.

For example, I wanted to add the Marginalia search engine on the home screen in this manner, but because of its default-src https://*.marginalia.nu policy, this doesn't work.

I was determined enough to do this, so I used Proxyman to alter the Marginalia CSP header, just enough to trick Safari into allowing the manifest URL. After adding the app on the home screen, it keeps working without any CSP alteration needed.

# Statusbar

By default, the status bar will take the color of the theme-color meta value.

If that doesn't exist, the status bar will be black with white text, which doesn't look too immersive on most apps.

You can make the status bar transparent so that it takes the background of the website using the following meta element:

<meta name=apple-mobile-web-app-status-bar-style content=black-translucent>

Note that if the website background is light, you'll still have white text in the status bar, which will be hard to read.