react-native-uxrate 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ name: PR Check
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ check:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: actions/setup-node@v4
13
+ with:
14
+ node-version: '18'
15
+ - run: npm install
@@ -0,0 +1,66 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - '[0-9]+.[0-9]+.[0-9]+'
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: actions/setup-node@v4
15
+ with:
16
+ node-version: '20'
17
+ registry-url: 'https://registry.npmjs.org'
18
+
19
+ - name: Publish to npm
20
+ run: npm publish --access public
21
+ env:
22
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
23
+
24
+ sync-public:
25
+ needs: publish
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+
30
+ - name: Checkout public repo
31
+ uses: actions/checkout@v4
32
+ with:
33
+ repository: ${{ github.repository_owner }}/UXRateSDK
34
+ token: ${{ secrets.PUBLIC_REPO_TOKEN }}
35
+ path: public-repo
36
+
37
+ - name: Sync docs
38
+ run: |
39
+ mkdir -p public-repo/docs/react-native
40
+ cp docs/public/*.md public-repo/docs/react-native/
41
+
42
+ - name: Sync tutorials
43
+ run: |
44
+ mkdir -p public-repo/docs/tutorials
45
+ for f in docs/tutorials/*.md; do
46
+ cp "$f" "public-repo/docs/tutorials/react-native-$(basename "$f")"
47
+ done
48
+
49
+ - name: Sync example app
50
+ run: |
51
+ rm -rf public-repo/examples/react-native
52
+ mkdir -p public-repo/examples/react-native
53
+ cp -r example/* public-repo/examples/react-native/
54
+
55
+ - name: Commit and push to public repo
56
+ working-directory: public-repo
57
+ run: |
58
+ git config user.name "github-actions[bot]"
59
+ git config user.email "github-actions[bot]@users.noreply.github.com"
60
+ git add -A
61
+ if git diff --cached --quiet; then
62
+ echo "No changes to commit"
63
+ else
64
+ git commit -m "sync react-native docs and example from ${{ github.ref_name }}"
65
+ git push
66
+ fi
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## 0.1.0
2
+
3
+ - Initial release
4
+ - iOS and Android support
5
+ - API: configure, identify, track, setScreen
package/LICENSE ADDED
@@ -0,0 +1,4 @@
1
+ Copyright (c) 2024–2026 SVUG Tech. All rights reserved.
2
+
3
+ This software is proprietary and confidential. Unauthorized copying,
4
+ distribution, or use of this software, via any medium, is strictly prohibited.
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # react-native-uxrate
2
+
3
+ React Native module wrapping the native iOS and Android UXRate SDKs. Provides
4
+ a thin JavaScript API that bridges to the platform-specific implementations
5
+ via React Native's NativeModules.
6
+
7
+ ## Architecture
8
+
9
+ ```
10
+ JS (index.js) --> NativeModules bridge --> iOS SDK (Swift)
11
+ --> Android SDK (Kotlin)
12
+ ```
13
+
14
+ The React Native layer is intentionally thin. All survey logic, session
15
+ replay, networking, and UI rendering happen inside the native SDKs. This
16
+ module exposes four methods that forward calls across the bridge:
17
+
18
+ - `configure` -- initialise the SDK with an API key and options
19
+ - `identify` -- associate the current user with a user ID and properties
20
+ - `track` -- record a custom event
21
+ - `setScreen` -- report the currently visible screen name
22
+
23
+ TypeScript definitions are included in `index.d.ts`.
24
+
25
+ ## Development
26
+
27
+ ```bash
28
+ git clone <repo-url>
29
+ cd react-native-uxrate
30
+ npm install
31
+ ```
32
+
33
+ ### Run the example app
34
+
35
+ ```bash
36
+ cd example
37
+ npm install
38
+ npx react-native run-ios # or run-android
39
+ ```
40
+
41
+ The example app demonstrates configure, identify, screen tracking with React
42
+ Navigation, and custom event tracking. See `example/App.tsx`.
43
+
44
+ ### Run tests
45
+
46
+ ```bash
47
+ npm test
48
+ ```
49
+
50
+ ## Documentation
51
+
52
+ - [Installation](docs/public/installation.md)
53
+ - [Quick Start](docs/public/quick-start.md)
54
+ - [Event Tracking](docs/public/event-tracking.md)
55
+ - [Session Replay Configuration](docs/public/replay-config.md)
56
+ - [API Reference](docs/public/api-reference.md)
57
+ - [React Navigation Integration](docs/tutorials/react-navigation-integration.md)
58
+
59
+ ## Release
60
+
61
+ 1. Bump version in `package.json`.
62
+ 2. Push a version tag (e.g. `0.2.0`).
63
+ 3. CI publishes the package to npm and syncs documentation, tutorials, and the
64
+ example app to the public repository.
65
+
66
+ ## License
67
+
68
+ Proprietary -- SVUG Tech
@@ -0,0 +1,43 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+
7
+ dependencies {
8
+ classpath 'com.android.tools.build:gradle:8.7.3'
9
+ classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.0'
10
+ }
11
+ }
12
+
13
+ apply plugin: 'com.android.library'
14
+ apply plugin: 'kotlin-android'
15
+
16
+ android {
17
+ namespace 'com.uxrate.reactnative'
18
+ compileSdk 35
19
+
20
+ defaultConfig {
21
+ minSdk 24
22
+ }
23
+
24
+ compileOptions {
25
+ sourceCompatibility JavaVersion.VERSION_17
26
+ targetCompatibility JavaVersion.VERSION_17
27
+ }
28
+
29
+ kotlinOptions {
30
+ jvmTarget = '17'
31
+ }
32
+ }
33
+
34
+ repositories {
35
+ google()
36
+ mavenCentral()
37
+ maven { url 'https://svug-tech.github.io/UXRateSDK' }
38
+ }
39
+
40
+ dependencies {
41
+ implementation 'com.facebook.react:react-native:+'
42
+ implementation 'com.uxrate:uxrate-sdk:[0.1.0, 0.2.0)'
43
+ }
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.uxrate.reactnative">
3
+ </manifest>
@@ -0,0 +1,105 @@
1
+ package com.uxrate.reactnative
2
+
3
+ import android.app.Application
4
+ import com.facebook.react.bridge.*
5
+ import com.uxrate.sdk.UXRate
6
+ import com.uxrate.sdk.models.OverlapStrategy
7
+ import com.uxrate.sdk.models.SDKTheme
8
+
9
+ /**
10
+ * React Native native module for the UXRate Android SDK.
11
+ *
12
+ * Exposes the same 4 methods as the iOS module:
13
+ * - configure(apiKey, options)
14
+ * - identify(userId, properties)
15
+ * - track(event)
16
+ * - setScreen(name)
17
+ */
18
+ class UXRateModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
19
+
20
+ override fun getName(): String = "UXRate"
21
+
22
+ @ReactMethod
23
+ fun configure(apiKey: String, options: ReadableMap, promise: Promise) {
24
+ try {
25
+ val application = reactApplicationContext.applicationContext as? Application
26
+ if (application == null) {
27
+ promise.reject("NO_APP", "Could not get Application instance")
28
+ return
29
+ }
30
+
31
+ val autoTrack = if (options.hasKey("autoTrackScreens")) {
32
+ options.getBoolean("autoTrackScreens")
33
+ } else false
34
+
35
+ val useMock = if (options.hasKey("useMockService")) {
36
+ options.getBoolean("useMockService")
37
+ } else false
38
+
39
+ val strategy = if (options.hasKey("overlapStrategy")) {
40
+ OverlapStrategy.from(options.getString("overlapStrategy"))
41
+ } else OverlapStrategy.SHOW_FIRST
42
+
43
+ val theme = if (options.hasKey("theme")) {
44
+ SDKTheme.from(options.getString("theme"))
45
+ } else SDKTheme.AUTO
46
+
47
+ UiThreadUtil.runOnUiThread {
48
+ UXRate.configure(
49
+ application = application,
50
+ apiKey = apiKey,
51
+ autoTrackScreens = autoTrack,
52
+ useMockService = useMock,
53
+ overlapStrategy = strategy,
54
+ theme = theme
55
+ )
56
+ promise.resolve(null)
57
+ }
58
+ } catch (e: Exception) {
59
+ promise.reject("CONFIGURE_ERROR", e.message, e)
60
+ }
61
+ }
62
+
63
+ @ReactMethod
64
+ fun identify(userId: String, properties: ReadableMap, promise: Promise) {
65
+ try {
66
+ val props = mutableMapOf<String, String>()
67
+ val iterator = properties.keySetIterator()
68
+ while (iterator.hasNextKey()) {
69
+ val key = iterator.nextKey()
70
+ props[key] = properties.getString(key) ?: ""
71
+ }
72
+
73
+ UiThreadUtil.runOnUiThread {
74
+ UXRate.identify(userId, props)
75
+ promise.resolve(null)
76
+ }
77
+ } catch (e: Exception) {
78
+ promise.reject("IDENTIFY_ERROR", e.message, e)
79
+ }
80
+ }
81
+
82
+ @ReactMethod
83
+ fun track(event: String, promise: Promise) {
84
+ try {
85
+ UiThreadUtil.runOnUiThread {
86
+ UXRate.track(event)
87
+ promise.resolve(null)
88
+ }
89
+ } catch (e: Exception) {
90
+ promise.reject("TRACK_ERROR", e.message, e)
91
+ }
92
+ }
93
+
94
+ @ReactMethod
95
+ fun setScreen(name: String, promise: Promise) {
96
+ try {
97
+ UiThreadUtil.runOnUiThread {
98
+ UXRate.setScreen(name)
99
+ promise.resolve(null)
100
+ }
101
+ } catch (e: Exception) {
102
+ promise.reject("SET_SCREEN_ERROR", e.message, e)
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,26 @@
1
+ package com.uxrate.reactnative
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ /**
9
+ * React Native package that registers the UXRate native module.
10
+ *
11
+ * Add this to your MainApplication's getPackages():
12
+ * ```kotlin
13
+ * override fun getPackages(): List<ReactPackage> = PackageList(this).packages.apply {
14
+ * add(UXRatePackage())
15
+ * }
16
+ * ```
17
+ */
18
+ class UXRatePackage : ReactPackage {
19
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
20
+ return listOf(UXRateModule(reactContext))
21
+ }
22
+
23
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
24
+ return emptyList()
25
+ }
26
+ }
@@ -0,0 +1,78 @@
1
+ # API Reference
2
+
3
+ All methods are available on the `UXRate` export:
4
+
5
+ ```tsx
6
+ import { UXRate } from 'react-native-uxrate';
7
+ ```
8
+
9
+ ## Methods
10
+
11
+ | Method | Description |
12
+ |--------|-------------|
13
+ | `configure(options)` | Initialise the SDK. Call once at app startup. |
14
+ | `identify(options)` | Identify the current user for survey targeting. |
15
+ | `track(options)` | Record a custom event. |
16
+ | `setScreen(name)` | Report the current screen name. |
17
+
18
+ All methods return `Promise<void>`.
19
+
20
+ ---
21
+
22
+ ### `configure(options: ConfigureOptions): Promise<void>`
23
+
24
+ ```ts
25
+ interface ConfigureOptions {
26
+ apiKey: string;
27
+ autoTrackScreens?: boolean; // default: false
28
+ useMockService?: boolean; // default: false
29
+ }
30
+ ```
31
+
32
+ | Parameter | Type | Required | Description |
33
+ |-----------|------|----------|-------------|
34
+ | `apiKey` | `string` | Yes | Your UXRate API key. |
35
+ | `autoTrackScreens` | `boolean` | No | Enable native auto screen tracking. In React Native apps manual `setScreen` calls are preferred. |
36
+ | `useMockService` | `boolean` | No | Use the built-in mock backend for local testing. |
37
+
38
+ ---
39
+
40
+ ### `identify(options: IdentifyOptions): Promise<void>`
41
+
42
+ ```ts
43
+ interface IdentifyOptions {
44
+ userId: string;
45
+ properties?: Record<string, string>;
46
+ }
47
+ ```
48
+
49
+ | Parameter | Type | Required | Description |
50
+ |-----------|------|----------|-------------|
51
+ | `userId` | `string` | Yes | A stable user identifier. |
52
+ | `properties` | `Record<string, string>` | No | Key-value properties for segment targeting. |
53
+
54
+ ---
55
+
56
+ ### `track(options: TrackOptions): Promise<void>`
57
+
58
+ ```ts
59
+ interface TrackOptions {
60
+ event: string;
61
+ }
62
+ ```
63
+
64
+ | Parameter | Type | Required | Description |
65
+ |-----------|------|----------|-------------|
66
+ | `event` | `string` | Yes | The event name to record. |
67
+
68
+ ---
69
+
70
+ ### `setScreen(name: string): Promise<void>`
71
+
72
+ | Parameter | Type | Required | Description |
73
+ |-----------|------|----------|-------------|
74
+ | `name` | `string` | Yes | The screen name to report. |
75
+
76
+ ## TypeScript
77
+
78
+ Full type definitions are shipped with the package in `index.d.ts`.
@@ -0,0 +1,60 @@
1
+ # Event Tracking
2
+
3
+ ## Custom events
4
+
5
+ Use `UXRate.track` to record events that can trigger surveys:
6
+
7
+ ```tsx
8
+ UXRate.track({ event: 'purchase_complete' });
9
+ UXRate.track({ event: 'onboarding_finished' });
10
+ ```
11
+
12
+ Event names are arbitrary strings. Define matching trigger rules in the UXRate
13
+ dashboard to show a survey when a specific event fires.
14
+
15
+ ## Screen tracking
16
+
17
+ React Native apps run inside a single native ViewController (iOS) or Activity
18
+ (Android), so the native auto-track feature sees only one screen. You should
19
+ report screens manually using `setScreen`.
20
+
21
+ ### Per-screen tracking with `useFocusEffect`
22
+
23
+ ```tsx
24
+ import { useFocusEffect } from '@react-navigation/native';
25
+ import { UXRate } from 'react-native-uxrate';
26
+
27
+ function SettingsScreen() {
28
+ useFocusEffect(() => {
29
+ UXRate.setScreen('Settings');
30
+ });
31
+
32
+ return <View>...</View>;
33
+ }
34
+ ```
35
+
36
+ ### Global tracking with React Navigation
37
+
38
+ You can report every navigation change from a single place:
39
+
40
+ ```tsx
41
+ import { NavigationContainer } from '@react-navigation/native';
42
+
43
+ <NavigationContainer
44
+ onStateChange={(state) => {
45
+ const route = state?.routes[state.index];
46
+ if (route) UXRate.setScreen(route.name);
47
+ }}
48
+ >
49
+ {/* screens */}
50
+ </NavigationContainer>
51
+ ```
52
+
53
+ See the [React Navigation integration tutorial](../tutorials/react-navigation-integration.md) for a full walkthrough.
54
+
55
+ ## Best practices
56
+
57
+ - Keep event names short and consistent (e.g. `snake_case`).
58
+ - Report screens as early as possible so survey targeting is accurate.
59
+ - Avoid tracking high-frequency events (e.g. scroll positions) -- they add
60
+ noise without improving targeting.
@@ -0,0 +1,48 @@
1
+ # Installation
2
+
3
+ ## Requirements
4
+
5
+ - React Native 0.70+
6
+ - iOS 17.0+ / Android API 24+
7
+
8
+ ## Install the package
9
+
10
+ ```bash
11
+ npm install react-native-uxrate
12
+ # or
13
+ yarn add react-native-uxrate
14
+ ```
15
+
16
+ ## iOS Setup
17
+
18
+ Install the CocoaPods dependency:
19
+
20
+ ```bash
21
+ cd ios && pod install
22
+ ```
23
+
24
+ The UXRate iOS SDK is pulled automatically via the podspec.
25
+
26
+ ## Android Setup
27
+
28
+ Add the UXRate Maven repository to your project-level `android/build.gradle`:
29
+
30
+ ```groovy
31
+ allprojects {
32
+ repositories {
33
+ maven { url 'https://svug-tech.github.io/UXRateSDK' }
34
+ }
35
+ }
36
+ ```
37
+
38
+ The module auto-links -- no additional native setup is needed.
39
+
40
+ ## Verify
41
+
42
+ Run your app on a device or simulator to confirm the module loads without errors:
43
+
44
+ ```bash
45
+ npx react-native run-ios
46
+ # or
47
+ npx react-native run-android
48
+ ```
@@ -0,0 +1,52 @@
1
+ # Quick Start
2
+
3
+ Get UXRate running in your React Native app in four steps.
4
+
5
+ ## 1. Configure the SDK
6
+
7
+ Call `configure` once at app startup, typically in your root `App.tsx`:
8
+
9
+ ```tsx
10
+ import { UXRate } from 'react-native-uxrate';
11
+
12
+ useEffect(() => {
13
+ UXRate.configure({ apiKey: 'YOUR_API_KEY' });
14
+ }, []);
15
+ ```
16
+
17
+ ## 2. Identify the user
18
+
19
+ After your user signs in, call `identify` so surveys can be targeted:
20
+
21
+ ```tsx
22
+ UXRate.identify({
23
+ userId: 'user-42',
24
+ properties: { plan: 'pro', company: 'Acme' },
25
+ });
26
+ ```
27
+
28
+ ## 3. Track events
29
+
30
+ Fire custom events that can trigger surveys:
31
+
32
+ ```tsx
33
+ UXRate.track({ event: 'checkout_complete' });
34
+ ```
35
+
36
+ ## 4. Report screen names
37
+
38
+ React Native runs inside a single native view controller / activity, so
39
+ automatic screen tracking will not distinguish between JS routes. Use
40
+ `setScreen` on each navigation change:
41
+
42
+ ```tsx
43
+ <NavigationContainer
44
+ onStateChange={(state) => {
45
+ const route = state?.routes[state.index];
46
+ if (route) UXRate.setScreen(route.name);
47
+ }}
48
+ >
49
+ ```
50
+
51
+ That's it -- surveys configured in the UXRate dashboard will now appear to
52
+ your users based on events, screens, and segments you define.
@@ -0,0 +1,33 @@
1
+ # Session Replay Configuration
2
+
3
+ ## Overview
4
+
5
+ Session replay is handled entirely by the native UXRate SDKs (iOS and Android).
6
+ The React Native module does not expose separate replay APIs -- replay capture
7
+ is started automatically by the native SDK when it is enabled for your project.
8
+
9
+ ## Enabling replay
10
+
11
+ 1. Open the UXRate dashboard.
12
+ 2. Navigate to your project settings.
13
+ 3. Toggle **Session Replay** on.
14
+ 4. Configure the desired sampling rate and privacy masking options.
15
+
16
+ Once enabled on the dashboard the native SDKs begin capturing replay data
17
+ without any additional code changes in your React Native app.
18
+
19
+ ## Privacy masking
20
+
21
+ Sensitive views can be masked from replays. Masking rules are configured in
22
+ the UXRate dashboard and applied at the native layer. Because React Native
23
+ renders through native view hierarchies, the standard masking rules apply
24
+ to RN-rendered content as well.
25
+
26
+ ## Troubleshooting
27
+
28
+ - **Replay not appearing**: Make sure the native SDK versions match the
29
+ minimum required for replay support (check the native SDK changelogs).
30
+ - **Missing frames**: Replay capture runs at a reduced frame rate to minimise
31
+ performance impact. Short interactions may not produce visible frames.
32
+ - **Large payload warnings**: If your app has complex view hierarchies,
33
+ consider increasing the masking scope to reduce payload size.
@@ -0,0 +1,132 @@
1
+ # React Navigation Integration
2
+
3
+ This tutorial walks through integrating UXRate screen tracking with React
4
+ Navigation in a React Native app.
5
+
6
+ ## Prerequisites
7
+
8
+ - `react-native-uxrate` installed and linked (see [Installation](../public/installation.md))
9
+ - `@react-navigation/native` and a navigator (e.g. `@react-navigation/native-stack`) installed
10
+
11
+ ## Step 1 -- Configure UXRate
12
+
13
+ In your root component, initialise the SDK:
14
+
15
+ ```tsx
16
+ import React, { useEffect } from 'react';
17
+ import { UXRate } from 'react-native-uxrate';
18
+
19
+ function App() {
20
+ useEffect(() => {
21
+ UXRate.configure({ apiKey: 'YOUR_API_KEY' });
22
+ }, []);
23
+
24
+ return (
25
+ // ...
26
+ );
27
+ }
28
+ ```
29
+
30
+ ## Step 2 -- Add global screen tracking
31
+
32
+ Use the `onStateChange` callback on `NavigationContainer` to report every
33
+ route change:
34
+
35
+ ```tsx
36
+ import { NavigationContainer } from '@react-navigation/native';
37
+
38
+ function App() {
39
+ // ... configure (Step 1)
40
+
41
+ return (
42
+ <NavigationContainer
43
+ onStateChange={(state) => {
44
+ const route = state?.routes[state.index];
45
+ if (route) {
46
+ UXRate.setScreen(route.name);
47
+ }
48
+ }}
49
+ >
50
+ <Stack.Navigator>
51
+ <Stack.Screen name="Home" component={HomeScreen} />
52
+ <Stack.Screen name="Profile" component={ProfileScreen} />
53
+ </Stack.Navigator>
54
+ </NavigationContainer>
55
+ );
56
+ }
57
+ ```
58
+
59
+ This reports the top-level route name on every navigation event.
60
+
61
+ ## Step 3 -- Track the initial screen
62
+
63
+ `onStateChange` does not fire for the initial route. To capture it, use a
64
+ navigation ref:
65
+
66
+ ```tsx
67
+ import { useRef } from 'react';
68
+ import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
69
+
70
+ function App() {
71
+ const navigationRef = useNavigationContainerRef();
72
+
73
+ return (
74
+ <NavigationContainer
75
+ ref={navigationRef}
76
+ onReady={() => {
77
+ const route = navigationRef.getCurrentRoute();
78
+ if (route) UXRate.setScreen(route.name);
79
+ }}
80
+ onStateChange={(state) => {
81
+ const route = state?.routes[state.index];
82
+ if (route) UXRate.setScreen(route.name);
83
+ }}
84
+ >
85
+ {/* screens */}
86
+ </NavigationContainer>
87
+ );
88
+ }
89
+ ```
90
+
91
+ ## Step 4 -- Fire custom events
92
+
93
+ Add event tracking to buttons or other interactions:
94
+
95
+ ```tsx
96
+ import { Button } from 'react-native';
97
+ import { UXRate } from 'react-native-uxrate';
98
+
99
+ function HomeScreen() {
100
+ return (
101
+ <Button
102
+ title="Complete Onboarding"
103
+ onPress={() => UXRate.track({ event: 'onboarding_complete' })}
104
+ />
105
+ );
106
+ }
107
+ ```
108
+
109
+ ## Step 5 -- Per-screen tracking (optional)
110
+
111
+ If you need per-screen side effects (analytics, etc.) you can also use
112
+ `useFocusEffect`:
113
+
114
+ ```tsx
115
+ import { useFocusEffect } from '@react-navigation/native';
116
+ import { UXRate } from 'react-native-uxrate';
117
+
118
+ function ProfileScreen() {
119
+ useFocusEffect(() => {
120
+ UXRate.setScreen('Profile');
121
+ });
122
+
123
+ return (/* ... */);
124
+ }
125
+ ```
126
+
127
+ ## Summary
128
+
129
+ - Use `onStateChange` + `onReady` on `NavigationContainer` for global
130
+ screen tracking.
131
+ - Use `UXRate.track` to fire custom events from any component.
132
+ - Optionally use `useFocusEffect` for per-screen logic.
@@ -0,0 +1,79 @@
1
+ import React, { useEffect } from 'react';
2
+ import { View, Text, Button, StyleSheet } from 'react-native';
3
+ import { NavigationContainer } from '@react-navigation/native';
4
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
5
+ import { UXRate } from 'react-native-uxrate';
6
+
7
+ const Stack = createNativeStackNavigator();
8
+
9
+ // ------------------------------------------------------------------
10
+ // Configure the UXRate SDK at app startup.
11
+ // Replace YOUR_API_KEY with your real key from the UXRate dashboard.
12
+ // For the dev environment use: https://app-dev.uxrate.com
13
+ // ------------------------------------------------------------------
14
+ function App(): React.JSX.Element {
15
+ useEffect(() => {
16
+ UXRate.configure({
17
+ apiKey: 'YOUR_API_KEY',
18
+ useMockService: true, // set false in production
19
+ });
20
+
21
+ UXRate.identify({
22
+ userId: 'demo-user-1',
23
+ properties: { plan: 'trial' },
24
+ });
25
+ }, []);
26
+
27
+ return (
28
+ <NavigationContainer
29
+ onStateChange={(state) => {
30
+ const route = state?.routes[state.index];
31
+ if (route) {
32
+ UXRate.setScreen(route.name);
33
+ }
34
+ }}
35
+ >
36
+ <Stack.Navigator initialRouteName="Home">
37
+ <Stack.Screen name="Home" component={HomeScreen} />
38
+ <Stack.Screen name="Products" component={ProductsScreen} />
39
+ </Stack.Navigator>
40
+ </NavigationContainer>
41
+ );
42
+ }
43
+
44
+ function HomeScreen({ navigation }: any): React.JSX.Element {
45
+ return (
46
+ <View style={styles.container}>
47
+ <Text style={styles.title}>Home Screen</Text>
48
+ <Button
49
+ title="Go to Products"
50
+ onPress={() => navigation.navigate('Products')}
51
+ />
52
+ <View style={styles.spacer} />
53
+ <Button
54
+ title="Track Button Tap"
55
+ onPress={() => UXRate.track({ event: 'button_tapped' })}
56
+ />
57
+ </View>
58
+ );
59
+ }
60
+
61
+ function ProductsScreen(): React.JSX.Element {
62
+ return (
63
+ <View style={styles.container}>
64
+ <Text style={styles.title}>Products Screen</Text>
65
+ <Button
66
+ title="Track Button Tap"
67
+ onPress={() => UXRate.track({ event: 'button_tapped' })}
68
+ />
69
+ </View>
70
+ );
71
+ }
72
+
73
+ const styles = StyleSheet.create({
74
+ container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
75
+ title: { fontSize: 24, marginBottom: 20 },
76
+ spacer: { height: 12 },
77
+ });
78
+
79
+ export default App;
@@ -0,0 +1,15 @@
1
+ const path = require('path');
2
+ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
3
+
4
+ const root = path.resolve(__dirname, '..');
5
+
6
+ const config = {
7
+ watchFolders: [root],
8
+ resolver: {
9
+ extraNodeModules: {
10
+ 'react-native-uxrate': root,
11
+ },
12
+ },
13
+ };
14
+
15
+ module.exports = mergeConfig(getDefaultConfig(__dirname), config);
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "react-native-uxrate-example",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "scripts": {
6
+ "start": "react-native start",
7
+ "android": "react-native run-android",
8
+ "ios": "react-native run-ios"
9
+ },
10
+ "dependencies": {
11
+ "@react-navigation/native": "^6.1.0",
12
+ "@react-navigation/native-stack": "^6.9.0",
13
+ "react": "18.2.0",
14
+ "react-native": "0.73.0",
15
+ "react-native-safe-area-context": "^4.8.0",
16
+ "react-native-screens": "^3.29.0",
17
+ "react-native-uxrate": "file:.."
18
+ },
19
+ "devDependencies": {
20
+ "@types/react": "^18.2.0",
21
+ "typescript": "^5.3.0"
22
+ }
23
+ }
package/index.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ export interface ConfigureOptions {
2
+ apiKey: string;
3
+ autoTrackScreens?: boolean;
4
+ useMockService?: boolean;
5
+ }
6
+
7
+ export interface IdentifyOptions {
8
+ userId: string;
9
+ properties?: Record<string, string>;
10
+ }
11
+
12
+ export interface TrackOptions {
13
+ event: string;
14
+ }
15
+
16
+ export declare const UXRate: {
17
+ configure(options: ConfigureOptions): Promise<void>;
18
+ identify(options: IdentifyOptions): Promise<void>;
19
+ track(options: TrackOptions): Promise<void>;
20
+ setScreen(name: string): Promise<void>;
21
+ };
package/index.js ADDED
@@ -0,0 +1,92 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ const LINKING_ERROR =
4
+ `The package 'react-native-uxrate' doesn't seem to be linked. Make sure to ` +
5
+ (Platform.OS === 'ios'
6
+ ? `run \`pod install\` in your iOS project directory.\n`
7
+ : `rebuild your Android project.\n`);
8
+
9
+ // Throw a helpful error if the native module is not linked
10
+ const NativeUXRate = NativeModules.UXRate
11
+ ? NativeModules.UXRate
12
+ : new Proxy(
13
+ {},
14
+ {
15
+ get() {
16
+ throw new Error(LINKING_ERROR);
17
+ },
18
+ }
19
+ );
20
+
21
+ /**
22
+ * React Native bridge for the UXRate SDK (iOS & Android).
23
+ *
24
+ * @example
25
+ * // Minimal setup (App.js / App.tsx)
26
+ * import { UXRate } from 'react-native-uxrate';
27
+ *
28
+ * useEffect(() => {
29
+ * UXRate.configure({ apiKey: 'uxr_xxx' });
30
+ * }, []);
31
+ *
32
+ * @example
33
+ * // Manual screen tracking
34
+ * useFocusEffect(() => {
35
+ * UXRate.setScreen('Home');
36
+ * });
37
+ */
38
+ export const UXRate = {
39
+ /**
40
+ * Initialise the UXRate SDK. Call once at app startup.
41
+ *
42
+ * @param {string} apiKey - Your UXRate API key (required).
43
+ * @param {boolean} [autoTrackScreens=false] - Auto-detect screens via
44
+ * UIViewController swizzle (iOS) or ActivityLifecycleCallbacks (Android).
45
+ * In React Native, manual setScreen() calls are preferred.
46
+ * @param {boolean} [useMockService=false] - Use the built-in mock backend.
47
+ */
48
+ configure({ apiKey, autoTrackScreens = false, useMockService = false }) {
49
+ if (Platform.OS !== 'ios' && Platform.OS !== 'android') return Promise.resolve();
50
+ return NativeUXRate.configure(apiKey, { autoTrackScreens, useMockService });
51
+ },
52
+
53
+ /**
54
+ * Identify the current user for segment-based survey targeting.
55
+ *
56
+ * @param {string} userId - A stable user identifier.
57
+ * @param {Object} [properties={}] - Optional key/value properties.
58
+ */
59
+ identify({ userId, properties = {} }) {
60
+ if (Platform.OS !== 'ios' && Platform.OS !== 'android') return Promise.resolve();
61
+ return NativeUXRate.identify(userId, properties);
62
+ },
63
+
64
+ /**
65
+ * Track a custom event. Used for event-based trigger rules.
66
+ *
67
+ * @param {string} event - The event name.
68
+ */
69
+ track({ event }) {
70
+ if (Platform.OS !== 'ios' && Platform.OS !== 'android') return Promise.resolve();
71
+ return NativeUXRate.track(event);
72
+ },
73
+
74
+ /**
75
+ * Report the current screen name. Call this on every navigation event.
76
+ *
77
+ * @param {string} name - The screen name to report.
78
+ *
79
+ * @example
80
+ * // With React Navigation
81
+ * <NavigationContainer
82
+ * onStateChange={(state) => {
83
+ * const route = state?.routes[state.index];
84
+ * if (route) UXRate.setScreen(route.name);
85
+ * }}
86
+ * >
87
+ */
88
+ setScreen(name) {
89
+ if (Platform.OS !== 'ios' && Platform.OS !== 'android') return Promise.resolve();
90
+ return NativeUXRate.setScreen(name);
91
+ },
92
+ };
@@ -0,0 +1,32 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ // Registers UXRateModule.swift with React Native's Objective-C bridge.
4
+ // Each RCT_EXTERN_METHOD must match an @objc method in UXRateModule.swift.
5
+
6
+ RCT_EXTERN_MODULE(UXRate, NSObject)
7
+
8
+ RCT_EXTERN_METHOD(
9
+ configure:(NSString *)apiKey
10
+ options:(NSDictionary *)options
11
+ resolve:(RCTPromiseResolveBlock)resolve
12
+ reject:(RCTPromiseRejectBlock)reject
13
+ )
14
+
15
+ RCT_EXTERN_METHOD(
16
+ identify:(NSString *)userId
17
+ properties:(NSDictionary *)properties
18
+ resolve:(RCTPromiseResolveBlock)resolve
19
+ reject:(RCTPromiseRejectBlock)reject
20
+ )
21
+
22
+ RCT_EXTERN_METHOD(
23
+ track:(NSString *)event
24
+ resolve:(RCTPromiseResolveBlock)resolve
25
+ reject:(RCTPromiseRejectBlock)reject
26
+ )
27
+
28
+ RCT_EXTERN_METHOD(
29
+ setScreen:(NSString *)name
30
+ resolve:(RCTPromiseResolveBlock)resolve
31
+ reject:(RCTPromiseRejectBlock)reject
32
+ )
@@ -0,0 +1,91 @@
1
+ import Foundation
2
+ import UXRateSDK
3
+
4
+ /// React Native bridge module that exposes the UXRate iOS SDK to JavaScript.
5
+ ///
6
+ /// Registered as `UXRate` via `RCT_EXTERN_MODULE` in `UXRateModule.m`.
7
+ /// All methods dispatch to `MainActor` since `UXRate` is `@MainActor`-isolated.
8
+ @objc(UXRate)
9
+ class UXRateModule: NSObject {
10
+
11
+ /// Initialise the UXRate SDK.
12
+ ///
13
+ /// - Parameters:
14
+ /// - apiKey: Your UXRate API key.
15
+ /// - options: Dictionary with optional keys:
16
+ /// - `autoTrackScreens` (Bool, default false)
17
+ /// - `useMockService` (Bool, default false)
18
+ @objc
19
+ func configure(
20
+ _ apiKey: String,
21
+ options: NSDictionary,
22
+ resolve: @escaping RCTPromiseResolveBlock,
23
+ reject: @escaping RCTPromiseRejectBlock
24
+ ) {
25
+ let autoTrack = options["autoTrackScreens"] as? Bool ?? false
26
+ let useMock = options["useMockService"] as? Bool ?? false
27
+ let strategy = OverlapStrategy.from(string: options["overlapStrategy"] as? String)
28
+ let theme = SDKTheme.from(string: options["theme"] as? String)
29
+
30
+ DispatchQueue.main.async {
31
+ UXRate.configure(
32
+ apiKey: apiKey,
33
+ autoTrackScreens: autoTrack,
34
+ useMockService: useMock,
35
+ overlapStrategy: strategy,
36
+ theme: theme
37
+ )
38
+ resolve(nil)
39
+ }
40
+ }
41
+
42
+ /// Identify the current user.
43
+ ///
44
+ /// - Parameters:
45
+ /// - userId: Stable user identifier.
46
+ /// - properties: Optional `[String: String]` attributes.
47
+ @objc
48
+ func identify(
49
+ _ userId: String,
50
+ properties: NSDictionary,
51
+ resolve: @escaping RCTPromiseResolveBlock,
52
+ reject: @escaping RCTPromiseRejectBlock
53
+ ) {
54
+ let props = properties as? [String: String] ?? [:]
55
+ DispatchQueue.main.async {
56
+ UXRate.identify(userId: userId, properties: props)
57
+ resolve(nil)
58
+ }
59
+ }
60
+
61
+ /// Track a custom event.
62
+ @objc
63
+ func track(
64
+ _ event: String,
65
+ resolve: @escaping RCTPromiseResolveBlock,
66
+ reject: @escaping RCTPromiseRejectBlock
67
+ ) {
68
+ DispatchQueue.main.async {
69
+ UXRate.track(event: event)
70
+ resolve(nil)
71
+ }
72
+ }
73
+
74
+ /// Report the current screen name.
75
+ @objc
76
+ func setScreen(
77
+ _ name: String,
78
+ resolve: @escaping RCTPromiseResolveBlock,
79
+ reject: @escaping RCTPromiseRejectBlock
80
+ ) {
81
+ DispatchQueue.main.async {
82
+ UXRate.setScreen(name)
83
+ resolve(nil)
84
+ }
85
+ }
86
+
87
+ /// React Native requires this to declare that the module does not have
88
+ /// a main queue setup requirement — methods are dispatched manually.
89
+ @objc
90
+ static func requiresMainQueueSetup() -> Bool { false }
91
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "react-native-uxrate",
3
+ "version": "0.2.0",
4
+ "description": "React Native module for the UXRate SDK — embed AI-powered surveys in your React Native app. Supports iOS and Android.",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/SVUG-Tech/react-native-uxrate"
10
+ },
11
+ "keywords": ["react-native", "ios", "android", "survey", "uxrate"],
12
+ "author": "UXRate <sdk@uxrate.app>",
13
+ "license": "MIT",
14
+ "peerDependencies": {
15
+ "react": "*",
16
+ "react-native": "*"
17
+ },
18
+ "react-native": "index.js",
19
+ "codegenConfig": {
20
+ "name": "RNUXRateSpec",
21
+ "type": "modules",
22
+ "jsSrcsDir": "."
23
+ }
24
+ }
@@ -0,0 +1,18 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = 'react-native-uxrate'
3
+ s.version = '0.1.0'
4
+ s.summary = 'React Native bridge for UXRate iOS SDK'
5
+ s.description = 'React Native module for the UXRate iOS SDK — embed AI-powered surveys in your React Native app.'
6
+ s.homepage = 'https://github.com/SVUG-Tech/UXRateSDK'
7
+ s.license = { :type => 'MIT' }
8
+ s.author = { 'UXRate' => 'sdk@uxrate.app' }
9
+ s.source = { :git => 'https://github.com/SVUG-Tech/react-native-uxrate.git', :tag => s.version.to_s }
10
+
11
+ s.ios.deployment_target = '17.0'
12
+ s.swift_version = '5.9'
13
+
14
+ s.source_files = 'ios/**/*.{swift,m,h}'
15
+
16
+ s.dependency 'React-Core'
17
+ s.dependency 'UXRateSDK', '~> 0.1.0'
18
+ end