I have implemented the Push and Notification API following this tutorial. It works perfectly fine in Chrome on Mac OS.
But now I am trying to get it to work on Safari on iOS (16.4.3). I have added my application to the home screen to make it a PWA.
I have a button #enable-notifications to execute the following code: JS code of my app
document.getElementById("enable-notifications").addEventListener("click", () => {
main();
});
const check = () => {
if (!('serviceWorker' in navigator)) {
throw new Error('No Service Worker support!')
}
if (!('PushManager' in window)) {
throw new Error('No Push API Support!')
}
}
const registerServiceWorker = async () => {
const swRegistration = await navigator.serviceWorker.register('/assets/js/order-dashboard/serviceworker.js');
return swRegistration;
}
const requestNotificationPermission = async () => {
Promise.resolve(Notification.requestPermission()).then(function(permission) {
if (permission !== 'granted') {
throw new Error('Permission not granted for Notification')
}
});
}
const main = async () => {
check();
const swRegistration = await registerServiceWorker();
const permission = await requestNotificationPermission();
}
const showLocalNotification = (title, body, swRegistration) => {
const options = {
body,
};
swRegistration.showNotification(title, options);
}
This is serviceworker.js:
// urlB64ToUint8Array is a magic function that will encode the base64 public key
// to Array buffer which is needed by the subscription option
const urlB64ToUint8Array = base64String => {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/')
const rawData = atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}
// saveSubscription saves the subscription to the backend
const saveSubscription = async subscription => {
const SERVER_URL = 'https://good-months-invite-109-132-150-239.loca.lt/save-subscription'
const response = await fetch(SERVER_URL, {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(subscription),
})
console.log(response);
return response.json()
}
self.addEventListener('activate', async () => {
// This will be called only once when the service worker is activated.
try {
const applicationServerKey = urlB64ToUint8Array(
'BDLVKNq32B-Dr3HRd4wQ2oNZL9mw5JAGhB1XGCdKlDE9_KDEw7uTOLuPKH-374RRolaa0rr7UyfrJd7tvRvp304'
)
const options = { applicationServerKey, userVisibleOnly: true }
const subscription = await self.registration.pushManager.subscribe(options)
const response = await saveSubscription(subscription)
console.log(response)
} catch (err) {
console.log('Error', err)
}
})
self.addEventListener("push", function(event) {
if (event.data) {
console.log("Push event!!! ", event.data.text());
showLocalNotification("Yolo", event.data.text(), self.registration);
} else {
console.log("Push event but no data");
}
});
const showLocalNotification = (title, body, swRegistration) => {
const options = {
body
// here you can add more properties like icon, image, vibrate, etc.
};
swRegistration.showNotification(title, options);
};
And this is my node.js back-end:
const express = require('express')
const cors = require('cors')
const bodyParser = require('body-parser')
const webpush = require('web-push')
const app = express()
app.use(cors())
app.use(bodyParser.json())
const port = 4000
app.get('/', (req, res) => res.send('Hello World!'))
const dummyDb = { subscription: null } //dummy in memory store
const saveToDatabase = async subscription => {
// Since this is a demo app, I am going to save this in a dummy in memory store. Do not do this in your apps.
// Here you should be writing your db logic to save it.
dummyDb.subscription = subscription
}
// The new /save-subscription endpoint
app.post('/save-subscription', async (req, res) => {
const subscription = req.body
await saveToDatabase(subscription) //Method to save the subscription to Database
console.log("saved");
res.json({ message: 'success' })
})
const vapidKeys = {
publicKey:
'BDLVKNq32B-Dr3HRd4wQ2oNZL9mw5JAGhB1XGCdKlDE9_KDEw7uTOLuPKH-374RRolaa0rr7UyfrJd7tvRvp304',
privateKey: 'BGbNIt2twl1XsbDHPNe_w6FrKsWcZrys6anByEKyCGo',
}
//setting our previously generated VAPID keys
webpush.setVapidDetails(
'mailto:myuserid@email.com',
vapidKeys.publicKey,
vapidKeys.privateKey
)
//function to send the notification to the subscribed device
const sendNotification = (subscription, dataToSend) => {
webpush.sendNotification(subscription, dataToSend)
}
//route to test send notification
app.get('/send-notification', (req, res) => {
const subscription = dummyDb.subscription //get subscription from your databse here.
const message = 'Hello World'
sendNotification(subscription, message)
res.json({ message: 'message sent' })
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
When I click #enable-notifications on iOS I get the pop-up to allow notifications. But then nothing happens. My back-end also does not get called.
What seems to be the issue here?
Edit: I have tested in on Safari Mac OS (works), Chrome MacOS (works), Chrome Windows (other device, works),... Only Safari iOS doesn't work.
Edit: navigator.serviceWorker.controller returns null.
from Push API not functioning on iOS 16.4.3
No comments:
Post a Comment