Skip to content

Repository files navigation

@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: 'a@example.ae' },
})

// 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

About

React Native SDK for Adfinia app

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages