Push Notifications
Send and handle push notifications
Overview
Supports push notifications via Firebase Cloud Messaging (FCM).
| Feature | Android | iOS |
|---|---|---|
| Get FCM token | ✓ | ✓ |
| Foreground receive | ✓ | ✓ |
| Notification click handling | ✓ | ✓ |
Supported Plan: Basic and above
Prerequisites
Android
Firebase project setup is required. Refer to the Dev Environment document to add google-services.json to your app.
The POST_NOTIFICATIONS permission ships enabled by default in AndroidManifest.xml (runtime permission request required on Android 13+). No uncommenting is needed.
iOS
APNs (Apple Push Notification service) setup is additionally required.
- Apple Developer → Certificates, Identifiers & Profiles → Keys → Create an APNs key (or issue an APNs certificate)
- Xcode → Select your project target → Signing & Capabilities →
+ Capability→ Add Push Notifications - Firebase Console → Project Settings → Cloud Messaging → Upload your APNs key (or certificate)
- Add
GoogleService-Info.plistto your Xcode project — APNs and FCM will be linked automatically.
Foreground notification behavior (iOS)
By default, iOS does not display notification banners when the app is in the foreground. The SDK handles this by calling the global function window.onFCMReceived(...), so you can implement your own in-app notification UI.
Get FCM Token
After the app launches, fetch the FCM token and save it on your server. Use this token to send push notifications to a specific device.
unveilyBridge.fetchFCMToken(cb) is a core method. cb is a callback function-name string that receives the result. Omit it to use the default 'onFCMTokenResult'. The callback is invoked with positional arguments (token, isSuccess).
window.unveilyBridge.fetchFCMToken("onFCMTokenResult");
function onFCMTokenResult(token, isSuccess) {
if (isSuccess && token) {
savePushToken(token);
}
}
async function savePushToken(token) {
await fetch('/api/push-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token })
});
}Server-side Token Storage
// Express.js
app.post('/api/push-token', async (req, res) => {
const { token } = req.body;
const userId = req.session.userId;
await db.query(
'INSERT INTO push_tokens (user_id, token) VALUES (?, ?) ON DUPLICATE KEY UPDATE token = ?',
[userId, token, token]
);
res.json({ ok: true });
});// ASP.NET Core
[HttpPost("push-token")]
public async Task<IActionResult> SavePushToken([FromBody] PushTokenRequest request)
{
var userId = User.GetUserId();
await _db.PushTokens.Upsert(new PushToken {
UserId = userId,
Token = request.Token
});
return Ok(new { ok = true });
}// Spring Boot
@PostMapping("/api/push-token")
public ResponseEntity<?> savePushToken(@RequestBody PushTokenRequest request,
@AuthenticationPrincipal UserDetails user) {
pushTokenService.upsert(user.getUsername(), request.getToken());
return ResponseEntity.ok(Map.of("ok", true));
}<?php
$data = json_decode(file_get_contents('php://input'), true);
$token = $data['token'];
$userId = $_SESSION['user_id'];
$stmt = $pdo->prepare(
'INSERT INTO push_tokens (user_id, token) VALUES (?, ?)
ON DUPLICATE KEY UPDATE token = ?'
);
$stmt->execute([$userId, $token, $token]);
header('Content-Type: application/json');
echo json_encode(['ok' => true]);<%
Dim token, userId
token = Request.Form("token")
userId = Session("user_id")
Dim conn, sql
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open Application("ConnectionString")
sql = "IF EXISTS (SELECT 1 FROM push_tokens WHERE user_id = " & userId & ") " &
"UPDATE push_tokens SET token = '" & token & "' WHERE user_id = " & userId &
" ELSE INSERT INTO push_tokens (user_id, token) VALUES (" & userId & ", '" & token & "')"
conn.Execute sql
Response.ContentType = "application/json"
Response.Write "{""ok"": true}"
%>Receiving and Click Handling
When a message is received, or the user taps a notification, the SDK calls the global function window.onFCMReceived(title, body, dataJson) that your web app defines. The third argument, dataJson, is a JSON string holding the custom data payload.
window.onFCMReceived = function (title, body, dataJson) {
const data = dataJson ? JSON.parse(dataJson) : {};
// Deep link (e.g. the URL to navigate to on notification tap)
if (data.url) {
window.location.href = data.url;
return;
}
// Foreground receive — show in-app notification UI
showInAppNotification({ title, body });
};Both foreground receive and notification clicks arrive through window.onFCMReceived (or a deep link). There is no 'unveily-event' CustomEvent and no addEventListener to use. Branch on a field such as url in the data payload to handle click navigation.
Custom data payload example
The value you put in data when sending is delivered as the dataJson string.
{
"title": "New order arrived",
"body": "Check order #1234",
"data": {
"url": "/orders/1234",
"orderId": "1234"
}
}