akyos/native-push
akyos/native-push is a Laravel package for sending native push notifications to mobile apps. It simplifies device token management and message delivery, making it easier to add iOS and Android push support to Laravel projects.
composer require akyos/native-push, then publish and review the config (config/native-push.php) and run the package migrations — you need the push_devices table before anything sends..p8, and the sandbox/production flag. Android: project ID plus the service-account JSON path. Both are optional and independent — start with whichever platform you can actually test on.php artisan native-push:check (credentials + connectivity) then php artisan native-push:test <device-token>. If check fails, nothing downstream matters.PushSubjectInterface / PushSubjectTrait → the Notifier transport → the devices controller and the native-push Stimulus controller.native-push://default transport, with a url() so tapping opens the order page.Recipient = entity. Implement PushSubjectInterface and use PushSubjectTrait on your user/entity, then send via NotifierInterface:
$notifier->send(
new PushMessage('Order shipped', $user, url: route('orders.show', $order))
);
The trait supplies the recipient's registered PushDevices, so a user with three devices fans out without extra work.
Event-driven, always queued. Trigger from domain events (OrderShipped, MessageReceived) via listeners that dispatch a ShouldQueue job. The transport performs a blocking HTTP/2 call per message; a synchronous loop over 10k users in a request will time out. One job per user, chunk() the device list.
Registration flow. The shipped Stimulus controller calls POST /native-push/devices after permission is granted and DELETE on logout. Protect those routes with your auth middleware group — check what the package registers by default and override it if it's open. The controller handles the token, but you own the "when to register" UX.
Per-platform opt-in. With only iOS configured, filter devices by platform before sending rather than letting Android sends fail per-message. Same in reverse.
Operations surface. native-push:devices for auditing/cleanup, native-push:send for scheduled digests or one-off broadcasts from cron/Tinker, native-push:check in your deploy pipeline as a post-deploy smoke test.
Testing. Never hit APNs/FCM in CI. Bind a fake/null notifier transport in the test container and assert on dispatched messages/queued jobs; keep one manual native-push:test run per environment as the real integration check.
laravel-doctrine entities, not plain Eloquent models. If your app is Eloquent, read the trait before adopting — it expects a stable identifier and a device relation. Wrap or adapt rather than assuming it drops onto App\Models\User.BadDeviceToken, which looks identical to a dead token. Confirm cleanup only fires on BadDeviceToken/410 Unregistered, never on 403, 429 or network errors — otherwise a wrong sandbox flag wipes your entire iOS device table in one send..p8 keys are shared, hosts are not. Dev builds and TestFlight behave differently. Verify which one you're on before debugging payloads.401 with an unrelated-looking message; clock skew on the host can do the same. native-push:check catches it early — run it after every deploy that touches env.php artisan config:cache after env changes; check reads the cached config, so a stale cache makes credential debugging maddening.url() needs app support. The deep link only works if the app was scaffolded with akyos/ux-native-cli --notification (or you implement the same payload key yourself). It also only fires on tap, never on delivery — don't use it as an analytics signal.419/401. If you support multiple clients, consider a token-auth variant of the route.DELETE the device or let reattribution happen.200 and an apns-id/message name only mean "queued". Store those IDs for support, and don't build "user saw it" logic on the send result.content-available silent pushes, Android data-only messages, per-platform TTS/badge): decorate or subclass the Notifier transport, and swap the device storage behind the model/config if you need soft deletes or audit history.How can I help you explore Laravel packages today?