npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@adfinia/sdk-react-native

v1.2.0

Published

Official Adfinia SDK for React Native / Expo - first-party event + identify ingestion and native push.

Downloads

55

Readme

@adfinia/sdk-react-native

Official Adfinia SDK for React Native / Expo. First-party event + identify ingestion and native push, with the same wire contract as @adfinia/sdk-web.

  • Pure TypeScript core - no native modules. Analytics works in Expo Go and any RN runtime.
  • Native push - FCM (Android) / APNs (iOS) via expo-notifications (requires a development build).
  • Offline-durable - events persist to AsyncStorage and retry with backoff.
  • Zero PII by default - device context is opt-in (autoContext: true).

Install

npx expo install @adfinia/sdk-react-native \
  @react-native-async-storage/async-storage expo-crypto \
  expo-localization expo-device expo-constants
# push only (optional):
npx expo install expo-notifications

Quick start

import { Adfinia } from '@adfinia/sdk-react-native'

// Once, at app start. The ingest host is baked in - do not configure it.
Adfinia.init({
  writeKey: 'pk_live_xxx',          // your tenant write key
  autoContext: true,                // opt in to device context
  consent: () => userAllowedAnalytics,
})

// Identify - PredictStreet keys identity by the wallet hash (external_id).
Adfinia.identify({
  externalId: walletHash,
  traits: { source: 'sdk_react_native', country: 'AE', email: '[email protected]' },
})

// Track product events.
Adfinia.track('order_placed', { market_id: 'fifwc-fra-sen-2026', stake_usd: 50 })

// Screens (the mobile equivalent of page views).
Adfinia.screen('Markets')

// Drain before a critical moment.
await Adfinia.flush()

// Logout.
Adfinia.reset()

API

| Method | Purpose | |--------|---------| | init(config) | Initialise. writeKey required. | | identify(idOrObj, traits?) | Attach identity (customerId / externalId) + traits. | | track(event, props?, opts?) | Record a product event. | | screen(name?, props?, opts?) | Record a screen view (primary view event on mobile). | | page(name?, props?, opts?) | Alias of screen for cross-SDK parity. | | setConsent(channels, status) | Write-only consent. channels is a single channel string or an array; status is 'opted_in' or 'opted_out'. Channels are open strings (not an enum) - the backend owns the valid-channel registry. Emits one consent_updated event with channels always an array. No read method by design. | | optIn(channels) | Shorthand for setConsent(channels, 'opted_in'). | | optOut(channels) | Shorthand for setConsent(channels, 'opted_out'). | | alias(newId, prevId?) | Merge an anonymous identity into a known one. | | reset() | Clear identity, mint a new anonymous id (logout). | | flush() | Drain the queue now. Returns a promise. | | registerPush(config?) | Register for native push (see below). | | notifications.list({ status?, cursor?, limit? }) | A page of in-app notifications: { data, nextCursor, hasMore }. See In-app inbox. | | notifications.markRead(id) / markAllRead() | Mark one / all read. Returns Promise<boolean>. | | notifications.subscribe(handler, options?) | Live subscription (SSE or long-poll). Returns { unsubscribe() }. | | notifications.trackOpened(n) / trackClicked(n) | Emit notification_opened / notification_clicked track events. |

opts carries a per-call externalId and a context map (caller wins on key collision).

Config

Adfinia.init({
  writeKey: string,          // required
  debug?: boolean,           // log internals to console.debug
  consent?: () => boolean,   // gate; false drops the call silently
  autoContext?: boolean,     // collect device context (default false)
  flushAt?: number,          // batch size that triggers a flush (default 50)
  flushIntervalMs?: number,  // flush interval (default 5000)
  maxQueueSize?: number,     // max buffered events (default 1000)
})

Native push

const res = await Adfinia.registerPush()
if (res.ok) {
  // token registered with Adfinia; backend dispatches via FCM/APNs
} else {
  // res.reason: 'unsupported' | 'permission_denied' | 'token_failed' | ...
}

registerPush() uses expo-notifications getDevicePushTokenAsync() to get the native device token (FCM on Android, APNs on iOS) and POSTs it to /api/v1/push/register. Requirements:

  1. expo-notifications installed.
  2. A development build (Expo Go cannot obtain native device tokens).
  3. FCM (google-services.json) configured for Android; an APNs key + the Push capability for iOS.

See the SDK publishing/push runbook for the full account + credential setup.

In-app inbox

Adfinia.notifications is a framework-agnostic data client for the in-app notification inbox. It ships no UI - you render the bell, list, and toasts; the SDK gives you typed notifications, a live subscription, and the open/click events.

// Page through the inbox (cursor-paginated).
const page = await Adfinia.notifications.list({ status: 'unread', limit: 20 })
if (page.hasMore) {
  const next = await Adfinia.notifications.list({ cursor: page.nextCursor! })
}

// Subscribe to live updates. On connect the handler fires once per
// currently-unread notification (replay), then once per new one.
const sub = Adfinia.notifications.subscribe((n) => {
  showToast(n) // { id, title, body, severity, deep_link?, ... }
})

// Record engagement + mark read when the user interacts.
Adfinia.notifications.trackOpened(n)    // -> notification_opened
await Adfinia.notifications.markRead(n.id)
Adfinia.notifications.trackClicked(n)   // -> notification_clicked
await Adfinia.notifications.markAllRead()

// Tear down on logout / unmount.
sub.unsubscribe()

The InboxNotification shape is { id, contact_id, title, body, severity('info'|'success'|'warning'|'error'), dismissable, deep_link?, data?, read, created_at, read_at?, expires_at? }.

Identity. The inbox is keyed on the current contact - contact_id resolves from the active identity in the server's order (customer_id > external_id > anonymous_id). Call identify(...) first, or pass { contactId } to any method.

Live transport. React Native has no built-in EventSource. subscribe() uses a real SSE stream when an EventSource implementation is available, and otherwise long-polls the unread list:

// Preferred: pass an EventSource (e.g. react-native-sse) for a true stream.
import EventSource from 'react-native-sse'
const sub = Adfinia.notifications.subscribe(showToast, { eventSource: EventSource })

// No EventSource? It long-polls automatically; tune the interval:
const sub2 = Adfinia.notifications.subscribe(showToast, { pollIntervalMs: 30_000 })

Both paths replay currently-unread on connect and de-dupe delivery by id, so the transport choice is invisible to your handler. Live SSE complements native push: registerPush() delivers background system notifications; the inbox stream powers the in-app list while the app is foregrounded.

Screen tracking

import { usePathname } from 'expo-router'
import { useAdfiniaScreenTracking } from '@adfinia/sdk-react-native'

function RootLayout() {
  useAdfiniaScreenTracking(usePathname())
  // ...
}

Wire contract

Identical to @adfinia/sdk-web: events POST to /api/v1/track[/batch] and /api/v1/identify[/batch] with fields customer_id, external_id, anonymous_id, event_name, properties, traits, context, occurred_at; auth via Authorization: Bearer <writeKey> and X-Adfinia-SDK-Version. Server identity resolution order: customer_id > external_id > anonymous_id.

License

MIT