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 (and custom events)#
This event does not fire for every push. Regular notifications (those with a
notificationblock — title/body that the OS draws into the system tray) do not dispatch any PHP event. The native handlers only dispatch a PHP event when the FCM payload is a data message and the data contains aneventkey naming the FQCN of the event class to dispatch.
In other words, PushNotificationReceived is just one possible event class — you opt in by sending its FQCN in the event data key. You can also send any custom event class of your own (e.g. App\Events\SyncNeeded); whatever class name you put in the event key is what gets dispatched.
When a qualifying data message arrives, the plugin boots an ephemeral PHP runtime on the device and dispatches the named event, so any standard Laravel listener will fire — including when the app is backgrounded or killed.
Triggering PushNotificationReceived
Send a data-only message with the event FQCN:
php artisan fcm:send {token} --data-only \ --d event="Native\\Mobile\\Events\\PushNotification\\PushNotificationReceived" \ --d payload='{"foo":"bar"}'
Or via the FCM v1 API:
{ "message": { "token": "device-token", "data": { "event": "Native\\Mobile\\Events\\PushNotification\\PushNotificationReceived", "payload": "{\"foo\":\"bar\"}" }, "apns": { "payload": { "aps": { "content-available": 1 } } } }}
Important:
#[OnNative]is a Livewire attribute. It only fires when the app is foregrounded and that Livewire component is currently mounted. For background data messages, register a regular Laravel listener instead.
Background-safe (Laravel listener)
use Illuminate\Support\Facades\Event;use Native\Mobile\Events\PushNotification\PushNotificationReceived; // In a service provider's boot()Event::listen(function (PushNotificationReceived $event) { // Runs in the ephemeral PHP runtime — works even when app is backgrounded. // Persist data, queue work, update local SQLite, etc.});
You can also register the listener via EventServiceProvider's $listen array, or as a class-based listener — anything Laravel auto-discovers will work.
Foreground only (Livewire)
If you only need to react while a specific component is on screen:
use Native\Mobile\Attributes\OnNative;use Native\Mobile\Events\PushNotification\PushNotificationReceived; #[OnNative(PushNotificationReceived::class)]public function handlePush($data){ // Only runs when this Livewire component is mounted in the foreground.}
What about regular notifications?
If you send a push with a notification block (title/body shown in the tray), the OS handles the display and no PHP event fires — not on tap, not on receipt. To run PHP code when a push arrives, send a data message with an event key as shown above. To navigate somewhere on tap, see Deep Linking.
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 Laravel event processing on the device without showing a visible notification. The event key in the data payload is required — its value is the FQCN of the event class the plugin dispatches. Without it, no PHP event fires. Use any class — your own (App\Events\…) or the built-in Native\Mobile\Events\PushNotification\PushNotificationReceived:
{ "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.