# Making a rastrillo app installable

Aviso does not own any of this, on purpose: a manifest is the app's
identity, and rastrillo's scaffold is not changed by the addon.
Android and desktop browsers deliver push to an ordinary website. iOS
and iPadOS (from 16.4) deliver it only to an app added to the Home
Screen, and only from that installed copy — so an app that wants push
on iPhones wants this recipe too. It is four things.

## 1. A manifest

Serve `manifest.webmanifest` (content type
`application/manifest+json`) with, at least:

```json
{
  "id": "/",
  "name": "Birthday Alarm",
  "short_name": "Birthdays",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "theme_color": "#5b6cff",
  "background_color": "#f6f7fb",
  "icons": [
    { "src": "/static/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/static/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}
```

`id` is what keeps an installed app the same app across a renamed
`start_url`; set it once and never change it. A twenty-line handler
that writes this from the app's name is enough — Eleven's
`serveManifest` is the model — and a static file is enough too.

## 2. Three tags in the layout's head

```html
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#5b6cff">
<link rel="apple-touch-icon" href="/static/icon-180.png">
```

Safari reads the Apple touch icon, not the manifest's icons, for the
Home Screen tile; 180 px is the size it wants.

## 3. The worker at its scope

Serve `sw.js` at the path whose scope it should control — `/sw.js`
for the whole app — with `Cache-Control: no-cache`, so a new worker is
noticed on the next load rather than after a cache expiry nobody
chose. Load the helper from inside it:

```js
importScripts("/static/aviso/aviso-sw.js");
```

The rest of the worker is in `SKILL.md`.

## 4. Coaching, keyed on `capabilities()`

`capabilities()` from `push.mjs` reports `standalone`: whether this
page is running as an installed app. On iOS and iPadOS, when it is
not, show the person how to add the app to their Home Screen (Share →
Add to Home Screen) and to sign in inside the installed copy before
pressing the enable button — Safari's cookies do not travel into the
installed app. Do not gate Android or desktop on installation; they
do not need it. Show the enable button only when `capabilities().push`
is true, and only once `navigator.serviceWorker.ready` has resolved.

That is the whole recipe. None of it is Web Push; all of it is what
Web Push needs on an iPhone, and a good idea everywhere else.
