# Service worker handlers in an iOS tab and app

> On iOS 18.6.2, 'onnotificationclick' in self was true in a worker controlled by a Home Screen app and false when a Safari tab controlled it.

import LabEvidenceLine from '@components/lab/LabEvidenceLine.astro';
import LabHarnessLinks from '@components/lab/LabHarnessLinks.astro';
import LabResults from '@components/lab/LabResults.astro';
import LabBacks from '@components/lab/LabBacks.astro';
import LabProvenance from '@components/lab/LabProvenance.astro';

<LabEvidenceLine behaviour="worker-event-handler-exposure-tab-vs-app" locale="en" />

The same service worker script exposes different notification and push members in a Safari tab
and a Home Screen app on iOS. On 2026-10-20, an iPhone 16 Pro running iOS 18.6.2 (22G100)
returned false in the tab and true in the app for `'onnotificationclick' in self`,
the check browser-compat-data (BCD) uses for `notificationclick`.

<span id="question"></span>

On iOS, does a service worker expose `onnotificationclick`, `onpush`, `Notification` and the
registration's `showNotification` and `pushManager` members when a Safari tab controls it, and when
a Home Screen web app controls it?

<span id="what-we-measured"></span>

## Worker probes in two contexts

One worker script, `lab-sw-v2` at `/lab/device-tests/sw.js` with scope `/lab/device-tests/`, was
read in two contexts:

- In Safari, a WebDriver automation window opened `/lab/device-tests/`, which registered the
  worker and became controlled by it (notebook N03). The automation window has its own empty website
  data store; feature detection does not depend on stored data.
- We added the same page to the Home Screen as "PWA Lab", launched it from its icon,
  and read it once before notification permission was granted (N18) and once after permission and a push
  subscription (N34).

The page sends the worker a probe message and prints the reply. In the served worker script
([sw.js](https://openpwa.net/lab/device-tests/sw.js)) the reply has two parts:

- read once when the worker script first runs, before the script assigns
  `self.onnotificationclick` (so the assignment cannot create the property being tested):
  `'onnotificationclick' in self`, `typeof self.onnotificationclick`, `'onnotificationclose' in self`,
  `'onpush' in self` and `'onpushsubscriptionchange' in self`;
- read when the probe arrives: `'Notification' in self`, `'showNotification' in registration`,
  `'pushManager' in registration` and `typeof clients.openWindow`.

## How to run

The tab read and the app read use the same page; Web Inspector is not needed.

1. In Safari on the iPhone, open `https://openpwa.net/lab/device-tests/` and wait until the context
   line shows a controlling service worker.
2. In "T7 and T8: notifications", tap **Probe worker exposure** and note each value it prints.
3. Tap Share, then **Add to Home Screen**, then **Add**. Launch **PWA Lab** from its icon.
4. Tap **Probe worker exposure** again and compare the two sets of values.
5. Optional: tap **1. Request permission**, allow it, and probe again, to see whether permission
   changes the app-side values.
6. Tap **Copy results as JSON** in each context to keep the values with the time and user agent;
   note the iOS build (Settings, General, About) separately, because the JSON does not include it.

<LabHarnessLinks behaviour="worker-event-handler-exposure-tab-vs-app" locale="en" />

## Results

On iOS 18.6.2 (22G100) the notification and push members in the table were absent from the worker
when a Safari tab controlled it and present when the Home Screen app did (Instrumented read, A;
notebook N03, N18, N34). In the app the members were already present before permission was
granted (N18) and the probe after permission returned the same values (N34), so granting permission
was not what exposed them.

| Expression | Worker controlled by a Safari tab (N03) | Worker controlled by the Home Screen app (N18, N34) |
|---|---|---|
| `'onnotificationclick' in self` | false | true |
| `typeof self.onnotificationclick` | undefined | object |
| `'onnotificationclose' in self` | false | true |
| `'onpush' in self` | false | true |
| `'onpushsubscriptionchange' in self` | false | true |
| `'Notification' in self` | false | true |
| `'showNotification' in registration` | false | true |
| `'pushManager' in registration` | false | true |
| `typeof clients.openWindow` | function | function |

In the app context the `notificationclick` handlers ran on 6 of 6 taps; see
[notificationclick in Home Screen web apps](/lab/behaviour/notificationclick-in-home-screen-apps/).

<LabResults behaviour="worker-event-handler-exposure-tab-vs-app" locale="en" />

<span id="what-the-documentation-says"></span>

## BCD detection and WebKit exposure rules

BCD records `api.ServiceWorkerGlobalScope.notificationclick_event` for `safari_ios` as
`version_added: false`
([ServiceWorkerGlobalScope.json](https://raw.githubusercontent.com/mdn/browser-compat-data/main/api/ServiceWorkerGlobalScope.json),
github.com, read 2026-10-20). In
[mdn/browser-compat-data#22959](https://github.com/mdn/browser-compat-data/issues/22959)
(github.com) a BCD contributor states that the collector detects the feature by evaluating
`"onnotificationclick" in self` in a service worker, that the check fails on Safari for iOS 16, 17
and 18, and that a WebKit preference gates the property. The measurement **adds a condition** to
that record: the check is false when a Safari tab controls the worker and true when a Home Screen
app controls it. If the collector runs its check in a browser tab, that explains the `false`; the
issue does not say which context the collector uses.

WebKit's iOS 16.4 post ties Web Push and notifications to web apps added to the Home Screen
([Web Push for Web Apps on iOS and iPadOS](https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/),
webkit.org), and the same BCD file notes for `push_event` that "Notifications are supported in web
apps saved to the home screen". Neither source says that the worker's own members differ by
controlling context. WebKit bug 252625, "NotificationEvent interface exists but Notification interface
does not on iOS 16.4", is resolved as fixed
([Bug 252625](https://bugs.webkit.org/show_bug.cgi?id=252625), bugs.webkit.org) and shows that
exposure on this platform has been inconsistent before. The Notifications API Standard defines the
`onnotificationclick` and `onnotificationclose` handlers on `ServiceWorkerGlobalScope`
([Notifications API](https://notifications.spec.whatwg.org/), whatwg.org) with no context condition.

As far as the study's prior-art check found, this split by controlling context had not been
published before.

## Data

The values are in the study's
[data package](/research/2026-10-ios18-home-screen-web-apps/data/) (CC BY 4.0): the tab side in
`raw/2026-10-20-ios18.6.2-tab-lab.json` (N03), the app side before permission in
`raw/2026-10-20-ios18.6.2-step12-app-lab.json` (N18) and after permission in
`raw/2026-10-20-ios18.6.2-step21-app-lab-exposure.json` (N34). The app-side files have a `.meta.json`
sidecar with the UTC read time, the tool hashes and the selected page. The machine-readable run is
in [/lab/results.json](/lab/results.json) under the id
`worker-event-handler-exposure-tab-vs-app/2026-10-20-ios-18.6.2-22g100`.

<span id="what-it-backs"></span>

## Related reference entries

The result backs the explanation of BCD's `notificationclick` entry on
[Web Push on iOS and iPadOS](/reference/notifications/ios-safari-push/) and the Home Screen
condition on the Safari for iOS row of the Web Push compatibility data.

<LabBacks behaviour="worker-event-handler-exposure-tab-vs-app" locale="en" />

## Limits

- One device, one build, one operator, one run day: an iPhone 16 Pro on iOS 18.6.2 (22G100), not
  wiped.
- The tab-side read came from a WebDriver automation window, which has its own empty data store. A
  Safari Web Extension of unknown identity was installed and runs in Safari's process, so it could
  affect tab-side reads.
- The tab and the app each have their own registration of the same script; this is a comparison of
  contexts, not of one worker instance.
- Only the members in the table were read. The five checks on the four event-handler properties are made once when the
  worker script starts, so a probe after permission repeats the values from that start; the records
  do not show whether the worker restarted in between. A WebKit feature flag could alter the values,
  and the device's feature-flag state was not recorded for this run.
- iPadOS, macOS Safari and other iOS builds were not tested. The same device is due to be rerun after
  its upgrade to iOS 27.

<LabProvenance locale="en" />