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
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
- Basic WebPush subscription code
- Generating VAPID key
- Installing PWA on iOS by adding to Home Screen
- Subscription and saving token (Prompt with permission)
- Service worker
- Sending push message
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
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 these commands in your terminal:
openssl ecparam -genkey -name prime256v1 -out vapid_private.pem
openssl ec -in vapid_private.pem -pubout -outform DER|tail -c 65|base64|tr -d '=' |tr '/+' '_-' >> vapid_public.txt
echo 'VAPID public:' ; cat vapid_public.txt
# Example: BCa4t85iJ0AYDG__5r48lo-HNdpi_29458t8R6zRTsF1OUi1QyvCRd_tOyXVkqH3nzsZdMzSRLlKJTXQyN7QI4s
openssl ec -in vapid_private.pem -outform DER|tail -c +8|head -c 32|base64|tr -d '=' |tr '/+' '_-' >> vapid_private.txt
echo 'VAPID private:' ; cat vapid_private.txt
# Example: Mz8GQ4Fx16dI-iEUZTp6KTLVsUrcIOfJmWWXlKb0Qgo
Then use it:
const VAPID_PUBLIC_KEY = 'BAwUJxIa7mJZMqu78Tfy2...';
let subscriptionOptions = {
userVisibleOnly: true,
applicationServerKey: VAPID_PUBLIC_KEY
};
See full example in frontend.js
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.
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
}
(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..."
}
}
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
You can send WebPush from frontend:
const title = "Push title";
const options = {
body: "Additional text with some description",
icon: "https://andreinwald.github.io/webpush-ios-example/images/favicon.png",
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Orange_tabby_cat_sitting_on_fallen_leaves-Hisashi-01A.jpg/1920px-Orange_tabby_cat_sitting_on_fallen_leaves-Hisashi-01A.jpg",
data: {
"url": "https://andreinwald.github.io/webpush-ios-example/success.html",
"message_id": "your_internal_unique_message_id_for_tracking"
},
};
navigator.serviceWorker.ready.then(function (serviceWorker) {
serviceWorker.showNotification(title, options);
});
Or from Backend, for example by using Node.js web-push library
See example in backend-sender.js
- Meet Web Push for Safari on developer.apple.com
- Sending web push notifications in web apps and browsers on developer.apple.com
- Vapid token standard
- VAPID RFC standard
- Webpush options like image and actions
- Web App Manifest for standalone mode
- The ServiceWorker lifecycle
- ServiceWorkerRegistration.pushManager is undefined
- applicationServerKey is not properly base64url-encoded
- undefined is not an object pushManager.subscribe
- User denied push permission