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.
- package/.github/workflows/pr-check.yml +15 -0
- package/.github/workflows/release.yml +66 -0
- package/CHANGELOG.md +5 -0
- package/LICENSE +4 -0
- package/README.md +68 -0
- package/android/build.gradle +43 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/uxrate/reactnative/UXRateModule.kt +105 -0
- package/android/src/main/java/com/uxrate/reactnative/UXRatePackage.kt +26 -0
- package/docs/public/api-reference.md +78 -0
- package/docs/public/event-tracking.md +60 -0
- package/docs/public/installation.md +48 -0
- package/docs/public/quick-start.md +52 -0
- package/docs/public/replay-config.md +33 -0
- package/docs/tutorials/react-navigation-integration.md +132 -0
- package/example/App.tsx +79 -0
- package/example/metro.config.js +15 -0
- package/example/package.json +23 -0
- package/index.d.ts +21 -0
- package/index.js +92 -0
- package/ios/UXRateModule.m +32 -0
- package/ios/UXRateModule.swift +91 -0
- package/package.json +24 -0
- package/react-native-uxrate.podspec +18 -0
|
@@ -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
package/LICENSE
ADDED
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,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.
|
package/example/App.tsx
ADDED
|
@@ -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
|