I agree to all guys, but, Apple Iphone rules. We need to adapt.
I don’t agree with that.
Don’t agree to what Maxime?
With what @FrancoisDigico said.
Activate feature without any glide’s support and let customer decide to use it or not, could be possible? glide would be not responsible to customers issues …
I think that’s an unlikely outcome. Glide’s philosophy is to offer a curated, cohesive out-of-the-box experience with limited customization. To draw an analogy, Glide is to Apple what Flutterflow is to Android Glide is to Flutterlow what Apple is to Android (I’m not sure the analogy is correct, but that’s more or less how I see it).
Providing notifications to some users but not others would not be very Apple-like, nor Glide-like for that matter.
Of course my whole analogy is confusing because Apple happens to be the linchpin in this story, but I digress.
I don’t work for Glide at all, I’m just sharing my thoughts and I could be totally wrong of course.
This is a MUST for us, all “alternatives” require users to install an extra app or even to share ids with us so we can properly trigger such notification, we need them back at least for antroid, is easier to say “sorry Apple doesn’t like them” vs “nope, not possible at all”
Glide vision is not to limit itself or limit features to specific devices. That is why I think they removed it.
Glide wants their platform to be cross platform without issues. But still, I think Apple will need to rollback their PWA restrictions… PWA is taking more space.
yes, they need to bring back asap!!! it’s imperative for apps that rely on communication! How will my users know they have a message or alert if they’re not inside the app. we all know attention spans are very small these days they need to be alerted! My app depends on it! I’m really upset and can’t see how my app with be useful without push notifications!
s/there/their/g
What is a glide app without push notifications? NOTHING
202505 We are still waiting)))
Dear All, @Robert_Petitto @NoCodeAndy @Darren_Murphy @Jeff_Hager @ThinhDinh,
I am using this Feature Requests to call on all Glide Experts to see if we can work on a good push notification solution I managed to get working on Laptops and on some phones like Samsnung.
OneSignal has an unlimited push notification on its free plan that I was able to explore. I got it working perfectly on Laptops and some phones, but I am sure that with the broad knowledge of our experts, we can work together to find a lasting solution while we wait for Glide.
I set up a Push notification app on OneSignal free account where I generated OneSignal App ID, OneSignal Safari Web ID, API Key and also a Webhook from Make which I used in the code I pasted below (with the help of ChatGPT of course). I saved the code as HTML file and upload on Netlfy (http://netlify.com) to generate a Glide link: https://xyz.netlify.app//notifications.html?glideRowId=2 and use the link in my Glideapp. The RowId=2 is because I used Google Sheet to target each user, but if you use Glide tables, you can use Glide Row ID.
What this will do is that when user clicked on the link, it will take them to the browser where they will click on the enable notification button. This will retrieve the Player ID for that device, send it through the webhook from Make and from there send it to Google sheet or Glide table.
I am available to answer any question on the process I followed and like I said this worked well on my Samsung phone and I was able to send push notifications smoothly, but for some reason it didn’t work on my Infinix phone. Maybe other phones might have the issue. It also worked well on my laptop as well.
So I am thinking that if we can all pull our knowledge and resources together, we will be able to get this to work on all devices. I am positive that we can do it.
See the code below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{PasteHere}} - Enable Notifications</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 500px;
margin: 0 auto;
padding: 20px;
text-align: center;
background-color: #f9f9f9;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
button {
background: #4CAF50;
color: white;
border: none;
padding: 12px 24px;
font-size: 16px;
border-radius: 8px;
cursor: pointer;
margin: 10px 0;
transition: background 0.3s;
}
button:hover {
background: #3e8e41;
}
button.secondary {
background: #f0f0f0;
color: #333;
}
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 20px auto;
display: none;
}
.status {
margin: 15px 0;
padding: 10px;
border-radius: 5px;
min-height: 50px;
}
.success {
background-color: #d4edda;
color: #155724;
}
.error {
background-color: #f8d7da;
color: #721c24;
}
.hidden {
display: none;
}
#recovery-options {
margin-top: 20px;
text-align: left;
}
#recovery-options ol {
text-align: left;
margin: 10px 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<img src="{{PasteHere}}" alt="Logo" class="logo" />
<h2>Enable Push Notifications for {{PasteHere}}</h2>
<p>Stay updated with announcements and events.</p>
<button id="enableBtn">Allow Notifications</button>
<div id="loader" class="loader"></div>
<div id="status" class="status"></div>
<div id="recovery-options" class="hidden">
<h3>Device Configuration Needed:</h3>
<div id="recovery-steps"></div>
<button onclick="retryWithAlternativeMethod()" class="secondary">Try Again</button>
<button onclick="window.location.reload()" class="secondary">Refresh Page</button>
</div>
<script>
const ONE_SIGNAL_APP_ID = "{{PasteHere}}";
const MAKE_WEBHOOK_URL = "{{PasteHere}}";
const DEVICE = {
isAndroid: /Android/.test(navigator.userAgent),
isIOS: /iPhone|iPad|iPod/.test(navigator.userAgent),
isOldChrome: /Chrome\/([0-9]+)/.test(navigator.userAgent) && parseInt(RegExp.$1) < 80,
isManufacturerBrowser: /SamsungBrowser|MiuiBrowser|HUAWEI/.test(navigator.userAgent),
hasDataSaver: navigator.connection?.saveData,
hasStorageAccess: testStorageAccess()
};
function testStorageAccess() {
try {
localStorage.setItem('test', '1');
localStorage.removeItem('test');
return true;
} catch {
return false;
}
}
const enableBtn = document.getElementById('enableBtn');
const loader = document.getElementById('loader');
const statusEl = document.getElementById('status');
const recoveryOptions = document.getElementById('recovery-options');
const recoverySteps = document.getElementById('recovery-steps');
function showLoader() {
loader.style.display = 'block';
enableBtn.style.display = 'none';
statusEl.textContent = "Setting up notifications...";
}
function hideLoader() {
loader.style.display = 'none';
}
function updateStatus(message, isSuccess = false) {
statusEl.textContent = message;
statusEl.className = isSuccess ? 'status success' : 'status error';
console.log(message);
}
async function sendToMake(playerId) {
const glideRowId = new URLSearchParams(window.location.search).get('glideRowId');
if (!glideRowId) {
updateStatus('Error: Missing user reference', false);
return false;
}
try {
const response = await fetch(MAKE_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
glideRowId: glideRowId,
playerId: playerId,
timestamp: new Date().toISOString()
})
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return true;
} catch (error) {
console.error('Webhook error:', error);
return false;
}
}
async function getPlayerIdStandard() {
return new Promise((resolve) => {
OneSignalDeferred.push(async (OneSignal) => {
try {
await OneSignal.init({
appId: ONE_SIGNAL_APP_ID,
safari_web_id: "{{PasteHere}}",
serviceWorkerParam: {
scope: "/",
enable: !DEVICE.isManufacturerBrowser
}
});
const subscription = OneSignal.User.PushSubscription;
if (subscription.id) {
resolve(subscription.id);
} else {
const listener = (event) => {
if (event.current.id) {
OneSignal.User.PushSubscription.removeEventListener('change', listener);
resolve(event.current.id);
}
};
OneSignal.User.PushSubscription.addEventListener('change', listener);
setTimeout(() => {
OneSignal.User.PushSubscription.removeEventListener('change', listener);
resolve(null);
}, 8000);
}
} catch (error) {
console.error('Standard method failed:', error);
resolve(null);
}
});
});
}
async function getPlayerIdIframeFallback() {
return new Promise((resolve) => {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.sandbox = 'allow-scripts allow-same-origin';
iframe.srcdoc = `
<script src="https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js"><\/script>
<script>
window.OneSignalDeferred = window.OneSignalDeferred || [];
OneSignalDeferred.push(async (OneSignal) => {
await OneSignal.init({
appId: "${ONE_SIGNAL_APP_ID}",
serviceWorker: false
});
const subscription = OneSignal.User.PushSubscription;
window.parent.postMessage({ playerId: subscription.id }, '*');
});
<\/script>
`;
const timeout = setTimeout(() => {
window.removeEventListener('message', listener);
iframe.remove();
resolve(null);
}, 8000);
const listener = (event) => {
if (event.data?.playerId) {
clearTimeout(timeout);
window.removeEventListener('message', listener);
iframe.remove();
persistPlayerId(event.data.playerId);
resolve(event.data.playerId);
}
};
window.addEventListener('message', listener);
document.body.appendChild(iframe);
});
}
function persistPlayerId(id) {
try {
localStorage.setItem('onesignal_player_id', id);
sessionStorage.setItem('onesignal_player_id', id);
document.cookie = `onesignal_player_id=${id}; max-age=2592000; path=/; SameSite=Lax`;
} catch (error) {
console.error('Persistence failed:', error);
}
}
function getPersistedPlayerId() {
try {
return (
localStorage.getItem('onesignal_player_id') ||
sessionStorage.getItem('onesignal_player_id') ||
document.cookie.match(/(^|;)onesignal_player_id=([^;]+)/)?.[2] ||
null
);
} catch {
return null;
}
}
async function initializeOneSignal() {
showLoader();
recoveryOptions.classList.add('hidden');
try {
if (!window.OneSignalDeferred) {
window.OneSignalDeferred = [];
const script = document.createElement('script');
script.src = 'https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js';
script.defer = true;
document.head.appendChild(script);
}
let playerId = await getPlayerIdStandard();
if (!playerId && (DEVICE.isAndroid || DEVICE.isManufacturerBrowser)) {
console.log('Trying iframe fallback...');
playerId = await getPlayerIdIframeFallback();
}
if (!playerId) {
playerId = getPersistedPlayerId();
if (playerId) console.log('Using persisted Player ID');
}
if (!playerId) throw new Error('Could not get Player ID');
const success = await sendToMake(playerId);
if (success) {
updateStatus("Success! Notifications enabled.", true);
if (DEVICE.isIOS) {
setTimeout(() => {
window.location.href = "{{PasteHere}}";
}, 2000);
}
} else {
throw new Error('Failed to save settings');
}
} catch (error) {
console.error("Error:", error);
showRecoverySteps();
updateStatus(`Error: ${error.message}`, false);
} finally {
hideLoader();
}
}
function showRecoverySteps() {
const solutions = [
DEVICE.isOldChrome && 'Update Chrome browser',
DEVICE.isManufacturerBrowser && 'Use standard Chrome browser instead',
DEVICE.hasDataSaver && 'Disable Data Saver mode in Chrome settings',
!DEVICE.hasStorageAccess && 'Enable cookies and site data'
].filter(Boolean);
recoverySteps.innerHTML = solutions.length
? `<ol>${solutions.map(s => `<li>${s}</li>`).join('')}</ol>`
: '<p>Please try again or contact support.</p>';
recoveryOptions.classList.remove('hidden');
}
async function retryWithAlternativeMethod() {
showLoader();
recoveryOptions.classList.add('hidden');
updateStatus("Trying alternative method...");
try {
const playerId = await getPlayerIdIframeFallback();
if (!playerId) throw new Error('Alternative method failed');
const success = await sendToMake(playerId);
if (success) {
updateStatus("Success! Notifications enabled.", true);
} else {
throw new Error('Failed to save settings');
}
} catch (error) {
updateStatus(`Error: ${error.message}`, false);
showRecoverySteps();
} finally {
hideLoader();
}
}
enableBtn.addEventListener('click', initializeOneSignal);
if (DEVICE.isIOS) {
statusEl.innerHTML = `
<strong>iPhone Users:</strong><br>
1. Tap "Allow Notifications"<br>
2. Accept the Safari permission prompt<br>
3. Open from home screen for best experience
`;
}
if (DEVICE.isAndroid) {
const issues = [
DEVICE.isManufacturerBrowser && 'manufacturer browser',
DEVICE.hasDataSaver && 'data saver mode',
!DEVICE.hasStorageAccess && 'storage access blocked'
].filter(Boolean);
if (issues.length) {
statusEl.innerHTML = `
<strong>Android Detected:</strong><br>
${issues.join(', ')} may affect notifications
`;
}
}
</script>
</body>
</html>
{{PasteHere}} - Enable Notifications
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 500px;
margin: 0 auto;
padding: 20px;
text-align: center;
background-color: #f9f9f9;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
button {
background: #4CAF50;
color: white;
border: none;
padding: 12px 24px;
font-size: 16px;
border-radius: 8px;
cursor: pointer;
margin: 10px 0;
transition: background 0.3s;
}
button:hover {
background: #3e8e41;
}
button.secondary {
background: #f0f0f0;
color: #333;
}
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 20px auto;
display: none;
}
.status {
margin: 15px 0;
padding: 10px;
border-radius: 5px;
min-height: 50px;
}
.success {
background-color: #d4edda;
color: #155724;
}
.error {
background-color: #f8d7da;
color: #721c24;
}
.hidden {
display: none;
}
#recovery-options {
margin-top: 20px;
text-align: left;
}
#recovery-options ol {
text-align: left;
margin: 10px 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
Enable Push Notifications for {{PasteHere}}
Stay updated with announcements and events.
Allow Notifications
Device Configuration Needed:
Try Again Refresh PageThe code may look scattered cos I remove some sensitive information, but I am available to answer any questions you might have. Thank you
Interesting. I’ll explore some time this summer.
Couldn’t agree more! It’s a shame paying for a non-code app dev tool without messages. Here is my workaround - FOR PERSONAL APPS ONLY - not suitable for apps with users outside a group/team/family.
Send an email to Pushover(free) using the “GMAIL Send Mail” integration . I have added the link to the message Body which at least takes the user to the list where the action was triggered..
In pushover you can configure multiple mail adresses for different devices and purposes. All devices must register in pushover under the same account and with the different mail adresses you can steer who receives messages with which priority.
And of course each GMAIL action comes with a cost of two updates (4ct $)
Anyway voted!
+1
Hurray! Thank you very much for the notifications. I am very happy about this event!
Out of curiosity, other no code solutions seem to have push notifications available natively for PWAs (Noloco for example).
I haven’t tested it but assuming it works as advertised - how come they can do it and Glide can’t?