npm.io
0.8.5 • Published yesterday

@pnlight/sdk-react-native

Licence
Commercial
Version
0.8.5
Deps
0
Size
2.5 MB
Vulns
0
Weekly
0

PNLight SDK - React Native Package

npm package

A React Native wrapper for the PNLight iOS SDK.

Installation

Install the package in your React Native or Expo app:

npm install @pnlight/sdk-react-native

or:

yarn add @pnlight/sdk-react-native

Then install iOS pods:

cd ios
pod install

Requirements

  • iOS 15.0+
  • React Native app with CocoaPods
  • Swift 5.7+

This package is iOS-only. Android is not supported.


iOS Setup

DivKit Pod Source

If you use RemoteUiView, add the DivKit CocoaPods source to your app's ios/Podfile before the default CocoaPods source:

source 'https://github.com/divkit/divkit-ios.git'
source 'https://cdn.cocoapods.org/'

Then run:

cd ios
pod install
Required Frameworks

Make sure the host iOS app links the frameworks required by PNLight:

  • StoreKit.framework
  • AdSupport.framework
  • AppTrackingTransparency.framework

For apps that need IDFA tracking, add NSUserTrackingUsageDescription to Info.plist:

<key>NSUserTrackingUsageDescription</key>
<string>This app uses device tracking to provide analytics and improve user experience.</string>

Usage

Initialization

Initialize PNLight before using analytics, attribution, or Remote UI:

import { initialize } from "@pnlight/sdk-react-native";

await initialize("your-api-key");
Event Logging
import { logEvent } from "@pnlight/sdk-react-native";

await logEvent("purchase_completed", {
  product_id: "premium_subscription",
  amount: 9.99,
  currency: "USD",
});
Attribution

Send attribution data from external providers before requesting UI config.

import { addAttribution } from "@pnlight/sdk-react-native";

const success = await addAttribution(
  "appsFlyer",
  { af_status: "Non-organic" },
  "your-appsflyer-id",
);
AppsFlyer Integration Example

PNLight does not depend on the AppsFlyer initialization order — it only needs the conversion data, delivered via addAttribution. Make sure the conversion ("attribution success") callback is not processed before PNLight is initialized: if it can fire earlier, store the conversion data in memory and call addAttribution once initialize completes.

AppsFlyer requires the ATT prompt to complete before it starts. If PNLight is initialized before ATT authorization, call updateIdfa() after the prompt completes so PNLight receives the granted IDFA.

import appsFlyer from "react-native-appsflyer";
import { requestTrackingPermission } from "react-native-tracking-transparency";
import {
  addAttribution,
  initialize,
  updateIdfa,
} from "@pnlight/sdk-react-native";

const getAppsFlyerUID = () =>
  new Promise<string | null>((resolve) => {
    appsFlyer.getAppsFlyerUID((error, uid) => {
      if (error) {
        console.error("AppsFlyer UID error", error);
        resolve(null);
        return;
      }

      resolve(uid);
    });
  });

export async function startSdks() {
  await initialize("your-api-key");

  // Request ATT, then pass the granted IDFA to PNLight.
  await requestTrackingPermission();
  await updateIdfa();

  // Start AppsFlyer after ATT completes (an AppsFlyer requirement).
  appsFlyer.onInstallConversionData(async (conversionData) => {
    const appsFlyerId = await getAppsFlyerUID();

    await addAttribution(
      "appsFlyer",
      conversionData?.data ?? conversionData,
      appsFlyerId,
    );
  });

  appsFlyer.initSdk(
    {
      devKey: "your-appsflyer-dev-key",
      appId: "your-ios-app-id",
      isDebug: false,
      onInstallConversionDataListener: true,
    },
    (result) => {
      console.log("AppsFlyer initialized", result);
    },
    (error) => {
      console.error("AppsFlyer init error", error);
    },
  );
}

Supported providers:

  • appsFlyer
  • firebase
  • facebook
User Identity
import { getUserId } from "@pnlight/sdk-react-native";

const userId = await getUserId();
In-App Purchases

PNLight wraps StoreKit 2 for fetching products (price, offers, trial info), purchasing, restoring, and checking entitlements. The product ids are configured on the backend — fetchProducts resolves them against the App Store.

import {
  fetchProducts,
  purchase,
  restorePurchases,
  isPremium,
  isEligibleForTrial,
} from "@pnlight/sdk-react-native";

// Load the configured products with their App Store price/offer info.
const products = await fetchProducts();
for (const product of products) {
  console.log(`${product.displayName}: ${product.displayPrice}`);

  const offer = product.subscription?.introductoryOffer;
  if (offer && product.subscription?.isEligibleForIntroOffer) {
    // e.g. pay-as-you-go: "$0.99/month for 6 months"
    console.log(
      `Offer: ${offer.displayPrice} (${offer.paymentMode}, ` +
        `${offer.periodCount} × ${offer.period.value} ${offer.period.unit})`,
    );
  }
}

// Purchase.
const result = await purchase("your.product.id");
if (result === "success") {
  // Unlock content.
}

// Entitlement checks (local StoreKit entitlements, work offline).
const premium = await isPremium();
const eligible = await isEligibleForTrial("your.product.id");

// Restore previous purchases.
await restorePurchases();

RemoteUiView - Server-driven UI

RemoteUiView fetches and renders a server-driven layout from PNLight for a given placement. It calls getUIConfig(placement) internally, renders the native view, and emits action events to JavaScript.

When using external attribution providers such as AppsFlyer, send attribution as early as possible (see the AppsFlyer example above). getUIConfig waits for attribution data internally when attributionRequired is true (the default), so no manual delay is needed.

import React from "react";
import { StyleSheet, View } from "react-native";
import { RemoteUiView, type ActionEvent } from "@pnlight/sdk-react-native";

export function PaywallScreen() {
  const handleAction = (event: ActionEvent) => {
    if (event.logId === "purchase_button") {
      const productId = event.params.id;
      // Start purchase flow for productId
    } else if (event.logId === "close_button") {
      // Close the screen
    }
  };

  return (
    <View style={styles.root}>
      <RemoteUiView
        placement="paywall"
        onAction={handleAction}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
});
Native iOS circular loader

Use DivKit's custom element to render a native UIActivityIndicatorView inside Remote UI markup:

{
  "type": "custom",
  "custom_type": "pnlight.circular_loader",
  "width": { "type": "fixed", "value": 48 },
  "height": { "type": "fixed", "value": 48 },
  "custom_props": {
    "style": "large",
    "color": "#FF007AFF",
    "accessibility_label": "Loading"
  }
}

style accepts "medium" (the default) or "large". color accepts #RRGGBB or DivKit-style #AARRGGBB and defaults to the adaptive iOS label color. accessibility_label defaults to "Loading". Standard DivKit width and height fields control the element's layout; set a dimension to { "type": "wrap_content" } to use the native indicator's intrinsic size for that dimension.

Manual Config Fetching

Use getUIConfig if you need to fetch the placement configuration yourself. When attributionRequired is true (the default), the SDK waits for attribution data internally before returning:

import { getUIConfig } from "@pnlight/sdk-react-native";

const config = await getUIConfig("paywall");
const configWithoutAttributionWait = await getUIConfig("paywall", false);

API Reference

SDK Methods
Method Description
initialize(apiKey, baseDomain?) Initialize the SDK with your API key and optional base domain
logEvent(eventName, eventArgs?) Log a custom event with optional arguments
addAttribution(provider, data?, identifier?) Send attribution data from AppsFlyer, Firebase, or Facebook
getUserId() Get or create a stable user identifier
updateIdfa() Send the current IDFA to PNLight after the ATT prompt completes
prefetchUIConfig(placement) Prefetch a UI config into the in-memory cache
getUIConfig(placement, attributionRequired?) Fetch a UI config; waits for attribution by default
fetchProducts() Load configured products with App Store price/offer/trial info
purchase(productId) Purchase a product; resolves a PNLightPurchaseResult
restorePurchases() Restore previous purchases by syncing with the App Store
isPremium() Whether the user has an active entitlement to any configured product
isPurchased(productId) Whether the user has an active entitlement to a specific product
isEligibleForTrial(productId) Whether the user is eligible for a product's introductory offer
getAppleReceipt() Base64 App Store receipt for server-side validation, or null if absent
RemoteUiView
Prop Type Description
placement string PNLight placement identifier
style StyleProp<ViewStyle> Optional React Native style
secure boolean Deprecated. Secure rendering is controlled by the backend response.
preventRecording boolean Deprecated. Capture blocking is controlled by the backend.
attributionRequired boolean Wait for attribution before returning config; defaults to true
onAction (event: ActionEvent) => void Called when a custom action is triggered
ActionEvent
Property Type Description
url string Full URL string of the triggered action
scheme string URL scheme
path string URL path component
params Record<string, string> Query parameters extracted from the URL
logId string | undefined Log ID for the triggered action
action string | undefined Raw action value when provided by the native view
UIConfig
Property Type Description
config string | null Remote UI JSON config
parameters Record<string, any> | null Placement parameters returned by PNLight
debug boolean | null | undefined Debug flag returned by PNLight

Support

For support and questions, visit docs.pnlight.app.

Keywords