Expo Push (Classic)

Upgrading to v5

What changed in native-notify v5 and how to upgrade, including the breaking change to the five follow helpers.

Breaking change: the follow helpers

v5 is a drop-in upgrade except for registerFollowMasterID, registerFollowerID, postFollowingID, unfollowMasterID and updateFollowersList. They now return a NativeNotifyActionResult object instead of a string:

interface NativeNotifyActionResult {
  success: boolean;
  status:
    | 'registered'
    | 'already_registered'
    | 'posted'
    | 'already_posted'
    | 'unfollowed'
    | 'not_following'
    | 'removed'
    | 'not_found'
    | 'error';
  message: string; // the string the previous versions returned
  error?: any; // the underlying failure, when status is 'error'
}

Before v5, the result was a plain string:

const result = await registerFollowMasterID('master-id', appId, appToken);
if (result === 'Follow Master Indie ID already registered.') {
  // ...
}

In v5 it's an object:

const result = await registerFollowMasterID('master-id', appId, appToken);

if (result.success || result.status === 'already_registered') {
  // registered now, or it already was — branch on result.status for details
}

if (result.status === 'error') {
  console.warn(result.message, result.error);
}

message still holds the text previous versions returned, so most upgrades replace string checks with result.status or result.success. Network failures now surface as status: 'error' with the error attached, instead of being misreported as "already registered".

What's new in v5

ExportWhat it does
NativeNotify.init({ appId, appToken })Sets your credentials once at module scope. appId and appToken are then optional everywhere: plain functions (registerIndieID, the follow, inbox and send functions) read it.
<NativeNotifyProvider appId appToken>The same for hooks and components, as a React provider. They read it first, then NativeNotify.init().
useNativeNotify()Reads the active { appId, appToken } config inside a component.
useNativeNotifyPress<T>()Cold-start-safe notification tap handling. See Push data & taps.
registerForPushNotificationsAsync()The raw token flow, exported. It never throws and reports { status, reason, expoPushToken, ... }.
getNotificationInboxPage() / getIndieNotificationInboxPage()One page of inbox rows plus the server's total row count: { rows, total }.

Also new, and not breaking:

  • registerNNPushToken(appId, appToken, options) takes an optional third argument: { onRegistered, onError, watchTokenRotation }.
  • The notification inbox syncs the unread count to the app icon badge (syncBadge, default true), and both inboxes support exact pagination through the new page functions.

v5.1: analytics

Adds opt-in analytics: trackScreen and useNativeNotifyScreenTracking (screen views), session tracking, push-open reporting, the stable deviceId option, and the analytics flags on NativeNotify.init and registerNNPushToken. Everything is off by default. See Analytics.

v5.1.1: token-rotation stability

The token-rotation listener skips no-op same-token events, checks at most once per minute, never overlaps, and re-registers only when the token payload changed. There are no API changes. Upgrade if you're on 5.0.x or 5.1.0.

v5.2: inbox read state, rich fields, Android channels

Every change is additive: existing calls make the same requests.

npm install native-notify@^5.2.0
  • Per-notification inbox read state. The prebuilt inbox accepts perNotificationRead (off by default), and useNotificationInbox() also returns perNotificationRead and markNotificationRead(). For custom UIs, the page functions take { perNotification: true } after take and skip, and two new helpers mark one row read. They resolve false without sending anything when notificationId is missing. See the indie and mass inbox pages.

    getNotificationInboxPage(appId, appToken, take, skip, { perNotification: true })
    getIndieNotificationInboxPage(subId, appId, appToken, take, skip, { perNotification: true })
    markMassNotificationRead(notificationId, appId?, appToken?)
    markIndieNotificationRead(notificationId, subId, appId?, appToken?)
    
  • Rich-field send helpers. sendMassNotification, sendIndieNotification, sendIndieGroupNotification and sendNotificationToFollowers pass subtitle, badge, ttl, interruptionLevel, categoryId, channelId, collapseId, contentAvailable, mutableContent and sound to every send path. setAndroidNotificationChannel() creates the channel a channelId refers to. See Rich notifications.

  • New types: RichPushFields, SendNotificationOptions, AndroidNotificationChannelOptions and PerNotificationReadOptions.

Fixes in 5.2 only change requests for apps that turned analytics on, which now send the events they asked for:

  • Analytics reports (screens, sessions, opens) work when the ids are passed only to registerNNPushToken(appId, appToken, { analytics }). 5.1 dropped them unless NativeNotify.init() had the ids. That call's flags and ids now apply before the components under it mount, so a cold-start tap or the first tracked screen isn't lost.
  • useNativeNotifyScreenTracking(() => currentRouteName) (React Navigation, or any navigator other than Expo Router) tracks every screen change. 5.1 tracked only the first.
  • sendMassNotification stamps the inbox date.

v5.3: paired-token registration

Additive. Registrations send a device's Expo and native tokens together as an optional devicePair, and you change no code. See Paired-token registration.