@marcolabs/expo-push
v0.1.0-alpha.0
Published
Expo push notification runtime: permission lifecycle, device-scoped token management, badge sync, routing adapters, config plugin.
Maintainers
Readme
@marcolabs/expo-push
Expo push notification runtime — soft-ask permission lifecycle, device-scoped token management, badge sync, and a config plugin that opens the app's OS notification settings directly.
Install
pnpm add @marcolabs/expo-push
# peers: expo-application, expo-constants, expo-device, expo-notificationsapp.json:
{ "plugins": ["@marcolabs/expo-push"] }Plugin options (all optional):
{
"plugins": [
["@marcolabs/expo-push", {
"moduleName": "NotificationSettingsLink",
"targetName": "MyApp",
"androidPackage": "com.example.app.notificationsettings"
}]
]
}moduleName— JS-visibleNativeModuleskey; pass the same value tocreatePushRuntime({ settingsNativeModuleName }).targetName— iOS target name. Defaults to the sanitized Xcode project name (app names like "my-app" become "myapp"), so only set this for non-standard target names.androidPackage— Java package for the generated Kotlin files (default:<android.package>.notificationsettings).
Runtime
import { createPushRuntime } from '@marcolabs/expo-push';
export const push = createPushRuntime({
backend: {
claimToken: (p) => api.claimPushToken(p), // atomic per token
releaseToken: (t) => api.releasePushToken(t),
fetchUnreadCount: (uid) => api.unreadCount(uid),
},
storage: asyncStorageAdapter,
reporter: { report: (e, ctx) => sentry.capture(e, ctx) },
});Create once at app entry — it sets the foreground NotificationHandler.
Permission model (soft-ask)
requestPermission()— OS dialog; call only behind an explicit CTA (iOS won't re-show it after denial). Creates the Android channel FIRST — Android 13+ shows no popup without one.syncTokenIfGranted(userId)— app-entry sync; never pops the dialog.
Token lifecycle
registerToken— 15s fetch timeout (iOS can hang forever without one), thenbackend.claimTokenwhich must atomically re-assign a token still bound to another account.clearToken— logout; releases THIS device's token while the session is still valid. Skipping this is how pushes leak across account switches.addPushTokenListener— rotation on reinstall/OS updates.
Settings deep link
primeOSSettingsVisibility()— iOS registers the app in Settings > Notifications before you link there (reinstall edge case).openNotificationSettings()— straight to this app's notification settings on both platforms, via the bundled config plugin's native module.
Listeners / badge
addNotificationListener, addNotificationResponseListener,
getLastNotificationResponse + clearLastNotificationResponse,
syncBadge(userId) (deduped per user while in flight).
