This document explains how to integrate the PageSense iOS SDK into a React Native application using the React Native Native Bridge.
The guide covers:
The final result allows React Native applications to call PageSense SDK functions directly from React Native code such as:
The React Native Native Bridge allows JavaScript code to communicate with platform-specific native code.
This enables React Native applications to use:
How it Works
a. JavaScript calls a function in NativeModules
b. React Native sends the request across the bridge
c. Native code executes the request
d. Native code returns the result as a Promise
React Native applications primarily execute JavaScript code, whereas the PageSense SDK for iOS is implemented in Swift. Since JavaScript cannot directly interact with Swift code, React Native provides a Native Bridge mechanism. This bridge enables communication between the JavaScript layer and native modules, allowing the app to call SDK functions, retrieve experiment variations, and track goals seamlessly from the React Native code.
PageSense iOS SDK React Native – Native Bridge Architecture
The PageSense SDK exposes the following functions.
Feature | Function | Description |
Initialize SDK | createNewPageSenseClient | Creates and initialises a PageSense client instance in the application. This client instance is responsible for communicating with the PageSense service and managing experiments for the user session. |
Activate Experiment | activateExperiment | Activates a specific experiment for the user and allocates them to an appropriate variation based on the experiment configuration. |
Get Variation Name | getVariationName | Retrieves the name of the variation assigned to the current user for a particular experiment, allowing the application to apply the corresponding UI or behaviour changes. |
Track Goal | trackGoal | Records and tracks conversion events or goals defined in the experiment, helping measure the effectiveness and performance of different variations. |
Ensure that the following tools are installed in the development environment to enable the use of the React Native Native Bridge for integrating the PageSense iOS SDK with React Native applications.
Packages Required:
Verify installation:
PageSense iOS SDK can be distributed using XCFramework.
a. PageSense iOS SDK (PageSenseFramework.xcframework) can be downloaded from the following repository:
https://github.com/zoho/ZohoPageSenseSDK
b. Navigate to the above repository and open the Frameworks folder.
c. Download the file PageSenseFramework.xcframework.zip and extract its contents.
d. Place the extracted PageSense XCFramework in the iOS project folder at the location:
/ProjectRoot/ios/Frameworks/PageSenseFramework.xcframework
e. Open the workspace in Xcode and add the PageSenseFramework.xcframework to the project.
f. In Xcode, navigate to the project Target settings.
g. Go to: Target → General → Frameworks, Libraries and Embedded Content
h. Add the PageSenseFramework.xcframework to this section.
i. Set the framework option to: Embed & Sign
Ensure that the required React Native libraries are installed in the iOS project using CocoaPods. These libraries are automatically installed when running the command pod install from the ios directory. The following React Native libraries must be available in the project as part of the CocoaPods installation:
In addition, ensure that the following iOS framework dependencies required for implementing the React Native Native Bridge are available:
These libraries enable the implementation of native modules in Swift or Objective-C and allow the PageSense iOS SDK to be exposed to the React Native application through the React Native Native Bridge.
React Native native modules are implemented in two parts:
a. Swift Implementation
b. Objective-C Bridge
The Objective-C layer exposes Swift methods to React Native.
Create the native module file for the PageSense iOS SDK using Swift. This file should be placed within the iOS application directory located inside the iOS folder of the React Native project's root directory.
/ProjectRoot/ios/SampleApp/PageSenseSDKModule.swift
a. To enable communication between Swift and Objective-C, create a bridging header at the following location:
/ProjectRoot/ios/SampleApp/SampleApp-Bridging-Header.h
b. Open the project in Xcode and navigate to Build Settings.
c. Locate Objective-C Bridging Header.
d. Set its value to: SampleApp/SampleApp-Bridging-Header.h
Create the React Native wrapper file to enable interaction between the React Native application and the native PageSense SDK implementation. This wrapper acts as an interface layer that exposes the native module methods to the JavaScript code in the React Native application.
The JavaScript wrapper file should be created at the following path within the project structure:
src/sdk/PageSenseSDKWrapper.js
import { NativeModules } from 'react-native';// Extract the native module exposed through the React Native bridge.const { PageSenseSDKModule } = NativeModules;/** * PageSenseSDKWrapper provides a JavaScript interface for interacting * with the PageSense SDK through the React Native Native Bridge. */class PageSenseSDKWrapper { /** * Initialise the PageSense SDK. Should be called once during application startup. * * @param {string} accountId - PageSense account identifier. * @param {string} sdkKey - SDK key used for authentication. * @param {string} projectName - Name of the PageSense project. * * @returns {Promise<boolean>} * Returns: * - true → SDK initialized successfully * - false → initialization failed */ async initialiseSDK(accountId, sdkKey, projectName) { try { this._validateString(accountId, 'accountId'); this._validateString(sdkKey, 'sdkKey'); this._validateString(projectName, 'projectName'); const result = await PageSenseSDKModule.initialiseSDK( accountId, sdkKey, projectName, ); return result === true; } catch (error) { console.log('[PageSenseSDK] initialiseSDK error:', error); return false; } } /** * Activates an experiment and returns the assigned variation. * * @param {string} experimentName - Name of the experiment. * @param {string} userId - Unique user identifier. * @param {Object.<string, string>} [attributes] - Optional user attributes. * * @returns {Promise<string|null>} * Returns: * - variation name → if assigned * - null → if no variation or error */ async activateExperiment(experimentName, userId, attributes) { try { this._validateString(experimentName, 'experimentName'); this._validateString(userId, 'userId'); this._validateAttributes(attributes); const safeAttributes = attributes || {}; const variation = await PageSenseSDKModule.activateExperiment( experimentName, userId, safeAttributes, ); return variation ?? null; } catch (error) { console.log('[PageSenseSDK] activateExperiment error:', error); return null; } } /** * Retrieves the variation name without activating the experiment. * * @param {string} experimentName - Name of the experiment. * @param {string} userId - Unique user identifier. * @param {Object.<string, string>} [attributes] - Optional user attributes. * * @returns {Promise<string|null>} * Returns: * - variation name → if available * - null → if not available or error */ async getVariationName(experimentName, userId, attributes) { try { this._validateString(experimentName, 'experimentName'); this._validateString(userId, 'userId'); this._validateAttributes(attributes); const safeAttributes = attributes || {}; const variation = await PageSenseSDKModule.getVariationName( experimentName, userId, safeAttributes, ); return variation ?? null; } catch (error) { console.log('[PageSenseSDK] getVariationName error:', error); return null; } } /** * Tracks a goal event for an experiment. * * @param {string} experimentName - Name of the experiment. * @param {string} userId - Unique user identifier. * @param {string} goalName - Name of the goal. * @param {Object.<string, string>} [attributes] - Optional goal attributes. * * @returns {void} */ trackGoal(experimentName, userId, goalName, attributes) { try { this._validateString(experimentName, 'experimentName'); this._validateString(userId, 'userId'); this._validateString(goalName, 'goalName'); this._validateAttributes(attributes); const safeAttributes = attributes || {}; PageSenseSDKModule.trackGoal( experimentName, userId, goalName, safeAttributes, ); } catch (error) { console.log('[PageSenseSDK] trackGoal error:', error); } } /** * Validates whether the given parameter is a non-empty string. * @private * @throws {Error} if param is not a non-empty string */ _validateString(param, paramName) { if (typeof param !== 'string' || param.trim() === '') { throw new Error( `[PageSenseSDK] Invalid ${paramName}: must be a non-empty string`, ); } } /** * Validates the attributes object to ensure it is a valid key-value pair structure. * @private * @throws {Error} if attributes is invalid */ _validateAttributes(attributes) { if (attributes === undefined || attributes == null) return; if (typeof attributes !== 'object' || Array.isArray(attributes)) { throw new Error('[PageSenseSDK] attributes must be a key-value object'); } for (const key in attributes) { if (typeof key !== 'string' || key.trim() === '') { throw new Error( '[PageSenseSDK] Invalid attribute key: must be a non-empty string', ); } const value = attributes[key]; if (typeof value !== 'string') { throw new Error( `[PageSenseSDK] Invalid value for attribute "${key}". Allowed type: string`, ); } } }}// Export a singleton instanceexport default new PageSenseSDKWrapper();PageSense SDK must be initialised a single time during the application's lifecycle, ideally when the app launches. For example, you can initialise the SDK in the main entry point of the React Native application, such as App.jsx or App.js, before rendering any components or executing experiment-related logic. This approach guarantees that the SDK is fully set up and available whenever it is needed within the app.
The experiment should be executed when the screen is loaded to ensure that users are allocated to the correct variation and any relevant changes in the user interface or functionality are applied immediately. This ensures that the experiment is triggered as soon as the screen becomes active, providing consistent behaviour and accurate variation assignment. For example, in a React Native component, you can call the experiment inside a lifecycle method such as useEffect in App.jsx or App.js or any relevant screen component, so it runs once when the screen mounts:
Goal tracking should be performed immediately after a user completes a specific action or event within the application. This ensures that conversions, interactions, or other key performance indicators are recorded in real time, providing accurate and timely data for analysis of experiment effectiveness. For example, after a user taps a button, submits a form, or completes a purchase, the corresponding goal should be sent to the PageSense SDK for tracking.
After following this guide, your React Native application will be able to:
This integration enables FullStack A/B experimentation capabilities inside React Native applications using the PageSense iOS SDK.