@asyncify-hq/react-native
v0.1.0
Published
Native push registration for Asyncify — one hook that wires FCM device tokens, permissions and token rotation on Android and iOS
Maintainers
Readme
@asyncify-hq/react-native
Native push registration for Asyncify: one hook that wires a device's FCM token into your Asyncify subscriber, asks for the OS notification permission, follows token rotation, and surfaces foreground messages. Built on React Native Firebase — the OS is the push receiver, so there is no service worker to run.
Install
npm install @asyncify-hq/react-nativeThis package has required peers — install and configure them too:
npm install @react-native-firebase/app @react-native-firebase/messagingFollow the React Native Firebase
getting-started guide to add your
google-services.json (Android) / APNs key (iOS) and the @react-native-firebase/app
config plugin. react and react-native are peers you already have.
Usage
Your backend mints a short-lived, single-subscriber token with
@asyncify-hq/node
(API keys never reach the device):
// backend
const { token } = await asyncify.subscriberToken(user.id);// app
import { usePushRegistration } from '@asyncify-hq/react-native';
function PushToggle({ token }: { token: string }) {
const push = usePushRegistration({
token,
apiUrl: 'https://api.your-deployment.com',
onForegroundMessage: (msg) => console.log('foreground push', msg),
});
if (!push.supported) return null;
return (
<Button
title={push.enabled ? 'Disable push' : 'Enable push'}
disabled={push.busy}
onPress={push.enabled ? push.disable : push.enable}
/>
);
}enable() prompts for the notification permission, reads the FCM token, and
registers the device. The hook also re-registers silently after a token
rotation (the server upsert is idempotent), so you call enable() once.
The localhost warning
apiUrl is required and must be reachable from the phone. On a device,
localhost / 127.0.0.1 is the phone itself — not your laptop. Point apiUrl
at a LAN IP or a public tunnel (e.g. the cloudflare URL that asyncify dev
prints), never localhost.
Hook API
const {
supported, // boolean — true on Android/iOS
permission, // 'granted' | 'denied' | 'prompt'
enabled, // boolean — true once the device is registered
busy, // boolean — an enable()/disable() is in flight
error, // string | null — last explicit-action error
enable, // () => Promise<void>
disable, // () => Promise<void>
} = usePushRegistration({ token, apiUrl, onForegroundMessage });Background and quit-state notifications are displayed by the OS automatically;
onForegroundMessage fires only while the app is open.
Tapping a notification
On native, the OS always launches the app when a notification is tapped —
there is no browser-opens-directly path like web push. When the tapped message
carries a clickUrl (set on the workflow's push step), the hook forwards it to
the system browser automatically, covering both a background tap and a tap that
cold-started the app. Pass openClickUrlOnTap: false to receive taps yourself
via the React Native Firebase APIs instead — the URL also rides in the message
data as clickUrl.
Sticky opt-out
disable() must survive an app relaunch — otherwise, since the OS notification
permission is still granted, the hook would silently re-register a fresh token
on the next launch and undo the user's opt-out. To make it stick, the hook
persists an opt-out marker (asyncify:push:opted-out) via
@react-native-async-storage/async-storage,
which is an optional peer: disable() sets it, enable() clears it, and a
launch that sees it skips the silent re-register. Install async-storage to get
this behavior:
npm install @react-native-async-storage/async-storageWithout async-storage the hook still works, but a disable() only lasts until
the next app launch (the marker can't be persisted). The marker stores an
opt-out, so users who never call disable() keep the zero-config
auto-register behavior with no storage library at all.
iOS note
The code is platform-neutral — the same hook drives Android and iOS. iOS additionally requires an APNs key uploaded in the Firebase console and a Mac build (Xcode / an Apple Developer account). This package has been verified live on Android only; iOS is wired but unverified.
MIT © Shubam Patil
