Push Notifications Plugin for NativePHP Mobile#
Push notifications via Firebase Cloud Messaging (FCM) for NativePHP Mobile apps. Handles device registration, token management, permission flow, deep linking from notifications, data-only messages with background processing, and server-side sending.
Works on iOS (APNs via FCM) and Android (FCM).
Installation#
composer require nativephp/mobile-firebase
Firebase Setup#
Android#
- Create a Firebase project at Firebase Console
- Add your Android app (use your app's package name)
- Download
google-services.jsonand place it in your project root - The plugin compiler copies it to the Android build automatically
iOS#
- In the same Firebase project, add your iOS app
- Download
GoogleService-Info.plistand place it in your project root - Enable Push Notifications capability in your Apple Developer account
- Upload your APNs key to Firebase Console > Project Settings > Cloud Messaging
- The plugin compiler handles Xcode configuration automatically
Server-Side#
To send push notifications from your server, you need a Firebase service account:
- Firebase Console > Project Settings > Service Accounts
- Click "Generate new private key"
- Save the JSON file and set the path in your
.env:
FIREBASE_CREDENTIALS=/path/to/service-account.json
Quick Start#
use Native\Mobile\Facades\PushNotifications; // 1. Check current permission status$status = PushNotifications::checkPermission(); // 2. Enroll for push notifications (prompts user if needed)PushNotifications::enroll(); // 3. Handle the token when it arrives
use Native\Mobile\Attributes\OnNative;use Native\Mobile\Events\PushNotification\TokenGenerated; #[OnNative(TokenGenerated::class)]public function handleToken(string $token){ // Send token to your server and store it for this user $this->user->update(['push_token' => $token]);}
Permission Flow#
Checking Permission#
Always check before prompting. This does not trigger a system dialog.
$status = PushNotifications::checkPermission();// Returns: "granted", "denied", "not_determined", "provisional", or "ephemeral"
| Status | Description |
|---|---|
granted |
User allowed notifications |
denied |
User denied — direct them to system Settings |
not_determined |
Never asked — safe to call enroll() |
provisional |
Quiet notifications allowed (iOS only) |
ephemeral |
App Clips only (iOS) |
Enrolling#
Requests permission (if not_determined) and registers for push notifications. On success, a TokenGenerated event fires with the device token.
PushNotifications::enroll();
If permission is already granted, calling enroll() skips the dialog and immediately fetches the token.
Getting the Token#
Retrieve the cached token at any time:
$token = PushNotifications::getToken();
Returns the FCM token on Android, APNs/FCM token on iOS, or null if not yet enrolled.
Events#
TokenGenerated#
Fired when a push token is available after enrollment.
PHP (Livewire)
use Native\Mobile\Attributes\OnNative;use Native\Mobile\Events\PushNotification\TokenGenerated; #[OnNative(TokenGenerated::class)]public function handleToken(string $token){ // Store token on your server for this user/device $this->user->update(['push_token' => $token]);}
JavaScript (Vue)
import { On, Off, Events } from '#nativephp';import { onMounted, onUnmounted } from 'vue'; const handleToken = ({ token }) => { // Send to your backend fetch('/api/push-token', { method: 'POST', body: JSON.stringify({ token }), });}; onMounted(() => On(Events.PushNotification.TokenGenerated, handleToken));onUnmounted(() => Off(Events.PushNotification.TokenGenerated, handleToken));
JavaScript (React)
import { On, Off, Events } from '#nativephp';import { useEffect } from 'react'; useEffect(() => { const handler = On(Events.PushNotification.TokenGenerated, ({ token }) => { sendTokenToServer(token); }); return () => Off(Events.PushNotification.TokenGenerated, handler);}, []);
PushNotificationReceived#
Fired when a data message arrives (background processing).
use Native\Mobile\Attributes\OnNative;use Native\Mobile\Events\PushNotification\PushNotificationReceived; #[OnNative(PushNotificationReceived::class)]public function handlePush($data){ // Process the incoming data}
Sending Notifications#
Test Command#
Send a test notification from your terminal:
# Basic notificationphp artisan fcm:send {token} --title="Hello" --body="World" # With deep linkphp artisan fcm:send {token} --title="New Order" --body="Order #123 ready" --url="/orders/123" # Data-only (silent, triggers background processing)php artisan fcm:send {token} --data-only --d event=App\\Events\\SyncNeeded --d payload='{"sync_id":42}' # With badge countphp artisan fcm:send {token} --title="Messages" --body="3 unread" --badge=3 # Custom data pairsphp artisan fcm:send {token} --title="Update" --d type=order --d order_id=55
Sending from PHP#
Use the FCM v1 API with your service account credentials:
use Illuminate\Support\Facades\Http; $response = Http::withToken($accessToken) ->post("https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send", [ 'message' => [ 'token' => $deviceToken, 'notification' => [ 'title' => 'Order Shipped', 'body' => 'Your order is on the way!', ], 'data' => [ 'url' => '/orders/123', ], ], ]);
Deep Linking#
Push notifications can navigate users to specific screens when tapped. Include a url or link key in the data payload:
{ "message": { "token": "device-token", "notification": { "title": "New Message", "body": "You have a new message" }, "data": { "url": "/messages/42" } }}
The app navigates to the URL on tap, even from a cold start.
Data Messages (Background Processing)#
Send data-only messages to trigger server-side Laravel event processing without showing a visible notification. Include an event key in the data payload:
{ "message": { "token": "device-token", "data": { "event": "App\\Events\\DataSyncRequested", "payload": "{\"sync_id\": 42}" }, "apns": { "payload": { "aps": { "content-available": 1 } } } }}
The plugin spins up an ephemeral PHP runtime on the device to dispatch the event, even when the app is in the background. Your Laravel event listeners run natively on the device.
JavaScript API#
import { PushNotifications, On, Events } from '#nativephp'; // Check permissionconst { status } = await PushNotifications.checkPermission(); // Enroll (prompts if not_determined)if (status === 'not_determined') { await PushNotifications.enroll();} // Get cached tokenconst { token } = await PushNotifications.getToken(); // Clear badgeawait PushNotifications.clearBadge(); // Listen for tokenOn(Events.PushNotification.TokenGenerated, ({ token }) => { console.log('Token:', token);});
Recommended Permission Flow#
Don't call enroll() immediately on app launch. Show a pre-prompt first:
- Call
checkPermission()to get current status - If
not_determined, show a custom UI explaining why notifications are valuable - User taps "Enable" — call
enroll() - System dialog appears
- If granted,
TokenGeneratedfires - Send token to your server
- If
denied, show a message directing users to Settings
public function requestPush(){ $status = PushNotifications::checkPermission(); if ($status === 'denied') { Dialog::toast('Enable notifications in Settings'); return; } PushNotifications::enroll();}
Platform Notes#
| Feature | iOS | Android |
|---|---|---|
| Permission prompt | System dialog via UNUserNotificationCenter | System dialog on API 33+ (auto-granted below) |
| Token type | APNs token → FCM token (if Firebase configured) | FCM token |
| Background data messages | Ephemeral PHP runtime | Ephemeral PHP runtime |
| Deep linking | Via url or link in data payload |
Via url or link in data payload |
| Badge | Set via APNs payload | OS-managed badge dots |
clearBadge() |
Resets to zero | Cancels all displayed notifications |
| Firebase config | GoogleService-Info.plist in project root |
google-services.json in project root |
Coexistence with Local Notifications#
This plugin works alongside nativephp/mobile-local-notifications. On iOS, the local notifications plugin chains its delegate to this plugin's delegate automatically — both receive their respective callbacks without conflict.
License#
MIT License. See LICENSE for details.