Skip to content

andreinwald/webpush-ios-example

Repository files navigation

WebPush for iOS sample code and demo site

WebPush - is browser technology that allows site developer send notifications from backend to subscribers. Now at iPhone and iPad!

Demo https://andreinwald.github.io/webpush-ios-example

Iphone prompt example



TL;DR iOS WebPush specifics

  • user required to add your site to Home Screen of his iPhone/iPad
  • manifest.json is required to set display: standalone
  • you don't need to register at apple.com to receive something like GCM_SENDER_ID
  • instead, you need to generate VAPID (pair of public and private keys)

webpush_safari_ios_wwdc_2022.mp4

More info

Basic WebPush subscription code

Example of basic subscription code, that works in Google Chrome and Firefox.

<html>
<body>
<button onclick="subscribe()">Subscribe</button>

<script>
    // You can use serviceworker.js from this repo
    // It should contain listeners for push and notificationclick events
    navigator.serviceWorker.register('/serviceworker.js');

    function subscribe() {
        navigator.serviceWorker.ready.then(async function (serviceWorker) {
            if (!serviceWorker.pushManager) {
                // Maybe iOS on iPhone or iPad - should ask for adding to Home Screen
                alert('pushManager is not enabled');
                return;
            }            
            let subscriptionOptions = {
                userVisibleOnly: true,
                applicationServerKey: '____INSERT_VAPID_PUBLIC_KEY_HERE_____'
            };
            let subscription = await serviceWorker.pushManager.subscribe(subscriptionOptions);
            console.log('Subscription token:', subscription.toJSON());
        });
    }
</script>
</body>
</html>

You can run it locally by creating index.html and serviceworker.js files with a simple html server:

npx http-server

Generating VAPID key

In example above you need to replace VAPID_PUBLIC_KEY to your own.

You don't need to register at apple.com to receive something like GCM_SENDER_ID, just generate VAPID key

  • All subscription tokens associated with that key, so if you change it - you may lose old subscribers
  • You MUST need generate your own VAPID keys!
  • Newer share your PRIVATE_VAPID_KEY. It should be stored in a safe storage

Run this command in your terminal for generating:
npx web-push generate-vapid-keys

# result will be like:
# Public Key: BAwUJxIa7mJZMqu78Tfy2vqbp1tFuj4KwX3gRuF2e_5WGB0tGnvCBGtvVDEa6YdjnjAors3E1WBlcCTow6pGg
# Private Key: Mmi54fYPtCgTQB1_8-QoH0xJOq3H6z8nBUG71t0ezCA

Then use it in frontend.js:

const VAPID_PUBLIC_KEY = 'BAwUJxIa7mJZMqu78Tfy2...';
let subscriptionOptions = {
    userVisibleOnly: true,
    applicationServerKey: VAPID_PUBLIC_KEY
};
// Newer share or put into frontend your PRIVATE_VAPID_KEY!

See full example in frontend.js

Installing PWA on iOS by adding to Home Screen

WebPush is Progressive Web App(PWA) feature so you need to ask user to enable PWA mode first.
On iOs devices it can be made with button "Add to Home Screen" in browser.

Require adding to Home Screen

Also don't forget to set display mode in manifest.json!
Manifest.json required to set "display: standalone", that called by Apple "Home Screen web app"
PushManager will appear in serviceWorker object only after adding site to Home screen at your iPhone.

<html>
<head>
    <link rel="manifest" href="manifest.json"/>
</head>
# ...

manifest.json:

{
  "name": "WebPush iOS example",
  "display": "standalone"
}

Next you can check that PWA is installed by this code:

if (window.navigator.standalone) {
    // now we can ask for subscription by pushManager.subscribe()
} else {
    // we should ask user to add our site home screen
}

Subscription and saving token

(Displaying Prompt with permission)
After registering Service Worker and providing VAPID_PUBLIC_KEY you can request user to subscribe.
Best practice will be to ask user about subscription in html popup first.
Then you can call:

let subscription = await pushManager.subscribe(subscriptionOptions);

See full example in frontend.js

After receiving subscription you're going to send it to backend via fetch or something.
You will need that for sending push message from backend
Examples how subscription token looks:

For desktop and mobile Safari:

{
  "endpoint": "https://web.push.apple.com/QGuQyavXutnMH...",
  "keys": {
    "p256dh": "BF6-hyiRMKKKiiH...",
    "auth": "lM6vKjBJ1UX..."
  }
}

And this will be for Google Chrome (FCM):

{
  "endpoint": "https://fcm.googleapis.com/fcm/send/eEsw5ryoAzo:APA91bHC...",
  "expirationTime": null,
  "keys": {
    "p256dh": "BKDBx7wkagZSlDsaT...",
    "auth": "zKa3taDY2VWoM4..."
  }
}

Service worker

For receiving, displaying push message and processing click on it - you need to use these service worker methods:

self.addEventListener('push', (event) => {
    let pushData = event.data.json();
    // ...
    self.registration.showNotification(pushData.title, pushData)
});

self.addEventListener('notificationclick', function (event) {
    clients.openWindow(event.notification.data.url)
    // ...
    // You can send fetch request to your analytics API fact that push was clicked
});

See full example in serviceworker.js

Sending push from backend

When user subscribed, you can send notification:

import webpush from 'web-push';

const pushSubscription = { 
    "endpoint": "https://fcm.googleapis.com/fcm/send/fXbyGY04zHY:APA91bE-EZI...",
    "expirationTime": null,
    "keys": {
        "p256dh": "BHqcQRz0HXwdZXZOT5GkQC_d5P1XFcevTkNPuJqh...", // token of specific user
        "auth": "o3SJkOwZFr7deVnT98..."
    }
};

let pushData = JSON.stringify({
    "title": "Push title",
    "body": "Additional text with some description",
    "data": {
        "url": "https://andreinwald.github.io/?page=success",
    }
});
webpush.sendNotification(pushSubscription, pushData);

See details in backend-sender.js

Declarative webpush

https://webkit.org/blog/16535/meet-declarative-web-push/

Declarative Web Push allows web developers to request a Web Push subscription and display user visible notifications without requiring an installed service worker. Service worker JavaScript can optionally change the contents of an incoming notification. Unlike with original Web Push, there is no penalty for service workers failing to display a notification; the declarative push message itself is used as a fallback in case the optional JavaScript processing step fails.

Resources:

Keywords:

  • ServiceWorkerRegistration.pushManager is undefined
  • applicationServerKey is not properly base64url-encoded
  • undefined is not an object pushManager.subscribe
  • User denied push permission

About

WebPush for IOS demo and code: VAPID, Home Screen, gcm_sender_id, serviceworker, iPhone, iPad

Topics

Resources

Stars

Watchers

Forks

Contributors