@revrag-ai/embed-react-native
Version:
A powerful React Native library for integrating AI-powered voice agents into mobile applications. Features real-time voice communication, intelligent speech processing, customizable UI components, and comprehensive event handling for building conversation
835 lines (612 loc) • 21.8 kB
Markdown
# Embed React Native SDK Integration Guide
## Overview
The Embed React Native SDK is a powerful voice-enabled AI agent library that provides real-time communication capabilities. This comprehensive guide will walk you through the complete integration process from installation to deployment.
## Table of Contents
1. [Installation](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
2. [Peer Dependencies](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
3. [Android Configuration](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
4. [iOS Configuration](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
5. [Babel Configuration](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
6. [SDK Initialization](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
7. [App Setup](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
8. [Event System](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
9. [Usage Examples](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
10. [FAQ & Troubleshooting](https://www.notion.so/Embed-React-Native-SDK-Integration-Guide-201b9b86659d80b38625fb72de8c8f0e?pvs=21)
## Installation
Install the Embed React Native SDK using your preferred package manager:
```bash
# Using npm
npm install @revrag-ai/embed-react-native
# Using yarn
yarn add @revrag-ai/embed-react-native
# Using pnpm
pnpm add @revrag-ai/embed-react-native
```
## Peer Dependencies
The SDK requires several peer dependencies to be installed in your project. Install all required dependencies:
```bash
# Install peer dependencies
npm install @livekit/react-native @livekit/react-native-webrtc
npm install @react-native-async-storage/async-storage
npm install react-native-gesture-handler react-native-reanimated
npm install react-native-linear-gradient lottie-react-native
npm install react-native-safe-area-context
# For iOS, run pod install
cd ios && pod install && cd ..
```
### Alternative installation commands:
**Using Yarn:**
```bash
yarn add @livekit/react-native @livekit/react-native-webrtc @react-native-async-storage/async-storage react-native-gesture-handler react-native-reanimated react-native-linear-gradient lottie-react-native react-native-safe-area-context
```
**Using pnpm:**
```bash
pnpm add @livekit/react-native @livekit/react-native-webrtc @react-native-async-storage/async-storage react-native-gesture-handler react-native-reanimated react-native-linear-gradient lottie-react-native react-native-safe-area-context
```
## Android Configuration
### 1. Android Manifest Permissions
Add the following permissions to your `android/app/src/main/AndroidManifest.xml`:
```xml
<manifest xmlns:android="<http://schemas.android.com/apk/res/android>">
<!-- Required permissions for Embed SDK -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.MICROPHONE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:hardwareAccelerated="true">
<!-- Your activities and other components -->
</application>
</manifest>
```
### 2. Build.gradle Configuration
Add Lottie dependency to your `android/app/build.gradle`:
```
dependencies {
implementation 'com.airbnb.android:lottie:6.0.1'
// ... other dependencies
}
```
### 3. ProGuard Configuration (if using ProGuard)
Add to your `android/app/proguard-rules.pro`:
```
# Embed SDK
-keep class com.revrag.embed.** { *; }
-keep class org.webrtc.** { *; }
-dontwarn org.webrtc.**
# Lottie
-keep class com.airbnb.lottie.** { *; }
```
## iOS Configuration
### 1. iOS Permissions
**🚨 CRITICAL:** Add the following permissions to your `ios/YourAppName/Info.plist`. **Missing `NSMicrophoneUsageDescription` will cause the app to crash when accessing the microphone:**
```xml
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for voice communication with AI agent</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
```
**⚠️ App Crash Fix:** If your app crashes with "attempted to access privacy-sensitive data without a usage description", ensure the `NSMicrophoneUsageDescription` key is present in your `Info.plist`.
### 2. Pod Installation
After installing peer dependencies, run:
```bash
cd ios
pod install
cd ..
```
### 3. iOS Build Settings (if needed)
If you encounter build issues, add these to your iOS project settings:
- Enable Bitcode: `NO`
- Build Active Architecture Only: `YES` (for Debug)
## Babel Configuration
**⚠️ CRITICAL: React Native Reanimated Configuration**
Add the React Native Reanimated plugin to your `babel.config.js`. **This must be the last plugin in the plugins array:**
```jsx
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
// ... other plugins
'react-native-reanimated/plugin', // ← This MUST be the last plugin
],
};
```
**❌ Common Mistake:**
```jsx
// DON'T DO THIS - other plugins after reanimated plugin will cause issues
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
'react-native-reanimated/plugin',
'some-other-plugin', // ← This will break reanimated
],
};
```
**✅ Correct Configuration:**
```jsx
// DO THIS - reanimated plugin as the last plugin
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
'some-other-plugin',
'another-plugin',
'react-native-reanimated/plugin', // ← Last plugin
],
};
```
After updating `babel.config.js`, clean your project:
```bash
# For React Native
npx react-native start --reset-cache
# For Expo (if applicable)
expo start --clear
```
## SDK Initialization
### 1. useInitialize Hook
Initialize the SDK at the root level of your application using the `useInitialize` hook:
```jsx
import { useInitialize } from '@revrag-ai/embed-react-native';
function App() {
const { isInitialized, error } = useInitialize({
apiKey: 'YOUR_API_KEY',
embedUrl: 'YOUR_ONWID_SERVER_URL',
});
if (error) {
console.error('SDK initialization failed:', error);
}
if (!isInitialized) {
// Show loading screen while initializing
return <LoadingScreen />;
}
// Your app components
return <YourApp />;
}
```
### 2. Configuration Options
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `apiKey` | string | ✅ | Your Embed API key |
| `embedUrl` | string | ✅ | Your Embed server URL |
## App Setup
### 1. Wrap App with GestureHandlerRootView
**⚠️ IMPORTANT:** You must wrap your entire app with `GestureHandlerRootView` for the SDK to work properly:
```jsx
import React from 'react';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { useInitialize } from '@revrag-ai/embed-react-native';
export default function App() {
const { isInitialized, error } = useInitialize({
apiKey: 'your_api_key_here',
embedUrl: '<https://your-embed-server.com>',
});
return (
<GestureHandlerRootView style={{ flex: 1 }}>
{/* Your app components */}
</GestureHandlerRootView>
);
}
```
### 2. Add EmbedButton Component
Add the floating voice agent button to your screen:
```jsx
import { EmbedButton } from '@revrag-ai/embed-react-native';
function MyScreen() {
return (
<View style={{ flex: 1 }}>
{/* Your screen content */}
<EmbedButton />
</View>
);
}
```
## Event System
The SDK provides a powerful event system for sending user context and application state to the AI agent.
### Available Events
The SDK exports the following event types:
```jsx
import { EventKeys } from '@revrag-ai/embed-react-native';
// Available event keys:
EventKeys.USER_DATA // 'user_data' - User identity and profile
EventKeys.SCREEN_STATE // 'state_data' - Application state and context
```
### Event Usage Rules
**🚨 CRITICAL REQUIREMENT:**
1. **USER_DATA event MUST be sent first** before any other events
2. **USER_DATA must include `app_user_id`** for user identification
3. **EmbedButton should only be rendered AFTER** USER_DATA event is sent
4. **SCREEN_STATE events** can only be sent after USER_DATA is established
### Event Methods
```jsx
import { embed, EventKeys } from '@revrag-ai/embed-react-native';
// Send events
await embed.Event(eventKey, data);
// Listen to events (optional)
embed.on(eventKey, callback);
```
### How Events are Triggered
Events are triggered using the `embed.Event()` method and automatically:
1. **Validate the event key** against allowed EventKeys
2. **Store user identity** from USER_DATA events
3. **Auto-append app_user_id** to subsequent events
4. **Send data to your server** via the configured API
5. **Trigger local event listeners**
```jsx
// Example event flow
try {
// Step 1: Send user data first (required)
await embed.Event(EventKeys.USER_DATA, {
app_user_id: 'user123',
name: 'John Doe',
email: 'john@example.com',
});
// Step 2: Send context data (app_user_id auto-added)
await embed.Event(EventKeys.SCREEN_STATE, {
screen: 'profile',
data: { plan: 'premium' },
});
} catch (error) {
console.error('Event error:', error);
}
```
## Usage Examples
### Complete Integration Example
```jsx
import React, { useEffect, useState } from 'react';
import { View, StyleSheet, Alert } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import {
useInitialize,
EmbedButton,
embed,
EventKeys
} from '@revrag-ai/embed-react-native';
export default function App() {
const [userDataSent, setUserDataSent] = useState(false);
const { isInitialized, error } = useInitialize({
apiKey: 'your_api_key_here',
embedUrl: '<https://your-embed-server.com>',
});
// Initialize user data when SDK is ready
useEffect(() => {
if (isInitialized && !userDataSent) {
initializeUserData();
}
}, [isInitialized, userDataSent]);
const initializeUserData = async () => {
try {
// STEP 1: Send user data first (REQUIRED)
await embed.Event(EventKeys.USER_DATA, {
app_user_id: 'user123', // Required field
name: 'John Doe',
email: 'john@example.com',
subscription: 'premium',
joinedDate: '2024-01-15',
});
setUserDataSent(true);
// STEP 2: Send initial screen state
await embed.Event(EventKeys.SCREEN_STATE, {
screen: 'home',
timestamp: new Date().toISOString(),
userActions: [],
});
console.log('User data and initial state sent successfully');
} catch (error) {
console.error('Failed to initialize user data:', error);
Alert.alert('Error', 'Failed to initialize voice agent');
}
};
// Send screen state updates
const updateScreenState = async (screenName, data = {}) => {
if (!userDataSent) {
console.warn('Cannot send screen state before user data');
return;
}
try {
await embed.Event(EventKeys.SCREEN_STATE, {
screen: screenName,
timestamp: new Date().toISOString(),
...data,
});
} catch (error) {
console.error('Failed to update screen state:', error);
}
};
// Handle initialization errors
if (error) {
console.error('SDK initialization failed:', error);
return (
<View style={styles.errorContainer}>
<Text>Failed to initialize voice agent</Text>
</View>
);
}
// Show loading while initializing
if (!isInitialized) {
return (
<View style={styles.loadingContainer}>
<Text>Initializing voice agent...</Text>
</View>
);
}
return (
<GestureHandlerRootView style={styles.container}>
<View style={styles.content}>
{/* Your app content */}
<YourAppComponents onScreenChange={updateScreenState} />
</View>
{/* Only render EmbedButton after user data is sent */}
{userDataSent && <EmbedButton />}
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
content: {
flex: 1,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
```
### Event Listening Example
```jsx
import { embed, EventKeys } from '@revrag-ai/embed-react-native';
// Listen for events (optional)
useEffect(() => {
// Listen for user data events
const unsubscribeUserData = embed.on(EventKeys.USER_DATA, (data) => {
console.log('User data updated:', data);
});
// Listen for screen state events
const unsubscribeScreenState = embed.on(EventKeys.SCREEN_STATE, (data) => {
console.log('Screen state changed:', data);
});
// Cleanup listeners
return () => {
// Note: Current version doesn't provide unsubscribe method
// This is for illustration of the API design
};
}, []);
```
### Advanced Usage with Navigation
```jsx
import { useEffect } from 'react';
import { useNavigation } from '@react-navigation/native';
import { embed, EventKeys } from '@revrag-ai/embed-react-native';
function NavigationListener() {
const navigation = useNavigation();
useEffect(() => {
const unsubscribe = navigation.addListener('state', (e) => {
const routeName = e.data.state.routes[e.data.state.index].name;
// Send screen state when navigation changes
embed.Event(EventKeys.SCREEN_STATE, {
screen: routeName,
timestamp: new Date().toISOString(),
navigationStack: e.data.state.routes.map(route => route.name),
}).catch(console.error);
});
return unsubscribe;
}, [navigation]);
return null;
}
```
## FAQ & Troubleshooting
### 🔴 Critical Issues
### Q: "react-native-reanimated not working" or animation issues
**A:** This is the most common issue. Ensure:
1. ✅ React Native Reanimated plugin is **the last plugin** in `babel.config.js`
2. ✅ Clear cache after babel config changes: `npx react-native start --reset-cache`
3. ✅ Restart Metro bundler completely
4. ✅ For iOS: `cd ios && pod install`
```jsx
// ✅ Correct babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
'react-native-reanimated/plugin', // ← MUST be last
],
};
```
### Q: "User identity not found" error
**A:** This error occurs when you try to send events before USER_DATA:
1. ✅ Send `USER_DATA` event first with `app_user_id`
2. ✅ Wait for the event to complete before sending other events
3. ✅ Only render `EmbedButton` after USER_DATA is sent
```jsx
// ❌ Wrong order
await embed.Event(EventKeys.SCREEN_STATE, { screen: 'home' }); // Error!
await embed.Event(EventKeys.USER_DATA, { app_user_id: 'user123' });
// ✅ Correct order
await embed.Event(EventKeys.USER_DATA, { app_user_id: 'user123' });
await embed.Event(EventKeys.SCREEN_STATE, { screen: 'home' }); // Works!
```
### Q: Microphone permission denied
**A:** Ensure permissions are configured:
**Android:**
- ✅ `RECORD_AUDIO` and `MICROPHONE` permissions in `AndroidManifest.xml`
- ✅ Request permissions at runtime for Android 6+
**iOS:**
- ✅ `NSMicrophoneUsageDescription` in `Info.plist`
- ✅ Provide user-friendly description
### Q: iOS App Crashes - "attempted to access privacy-sensitive data without a usage description"
**A:** This crash occurs when the app tries to access the microphone without proper permission description:
**Quick Fix:**
1. ✅ Open `ios/YourAppName/Info.plist`
2. ✅ Add the microphone usage description:
```xml
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for voice communication with AI agent</string>
```
1. ✅ Clean and rebuild: `cd ios && pod install && cd .. && npx react-native run-ios`
**Why this happens:** iOS requires apps to declare why they need access to privacy-sensitive data like microphone, camera, location, etc.
### 🟡 Common Issues
### Q: EmbedButton not appearing
**A:** Check these requirements:
1. ✅ App wrapped with `GestureHandlerRootView`
2. ✅ SDK initialized successfully (`isInitialized` is true)
3. ✅ USER_DATA event sent first
4. ✅ EmbedButton rendered after USER_DATA
### Q: Network/API connection issues
**A:** Verify configuration:
1. ✅ Valid `apiKey` and `embedUrl`
2. ✅ Network connectivity
3. ✅ Server is accessible from the device
4. ✅ `usesCleartextTraffic="true"` for HTTP endpoints (Android)
### Q: iOS Network Request Failures - "The resource could not be loaded"
**A:** This is caused by iOS App Transport Security (ATS). Solutions:
**For HTTP APIs (Development/Testing):**
Add domain exceptions to `ios/YourApp/Info.plist`:
```xml
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<!-- Replace with your API domain -->
<key>your-api-domain.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
<!-- For localhost development -->
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
```
**For Production (Recommended):**
1. ✅ Use HTTPS endpoints instead of HTTP
2. ✅ Get proper SSL certificates
3. ✅ Update `embedUrl` to use `https://`
**⚠️ Never use `NSAllowsArbitraryLoads: true` in production apps**
### Q: Network debugging on iOS
**A:** Enable network debugging:
1. **Add logging to API calls:**
```jsx
// Add this to your API initialization
console.log('API URL:', embedUrl);
console.log('Making request to:', `${embedUrl}/embedded-agent/initialize`);
```
1. **Check iOS Console logs:**
- Open Xcode → Window → Devices and Simulators
- Select your device → Open Console
- Look for network-related errors
2. **Test network connectivity:**
```bash
# Test if your API is reachable
curl -I <http://your-api-domain.com/embedded-agent/initialize>
```
### 🟢 Best Practices
### Q: How to handle SDK initialization in different app states?
**A:** Best practices for initialization:
```jsx
const [initState, setInitState] = useState('loading');
const { isInitialized, error } = useInitialize({
apiKey: process.env.ONWID_API_KEY,
embedUrl: process.env.ONWID_URL,
});
useEffect(() => {
if (error) {
setInitState('error');
} else if (isInitialized) {
setInitState('ready');
}
}, [isInitialized, error]);
// Render based on state
switch (initState) {
case 'loading': return <LoadingScreen />;
case 'error': return <ErrorScreen error={error} />;
case 'ready': return <AppWithOnwid />;
}
```
### Q: How to optimize event sending?
**A:** Event optimization strategies:
```jsx
// ✅ Debounce frequent events
const debouncedStateUpdate = useCallback(
debounce((data) => {
embed.Event(EventKeys.SCREEN_STATE, data);
}, 500),
[]
);
// ✅ Batch related data
const sendUserProfile = async (userData) => {
await embed.Event(EventKeys.USER_DATA, {
app_user_id: userData.id,
...userData.profile,
...userData.preferences,
lastLogin: new Date().toISOString(),
});
};
```
### Q: How to handle offline scenarios?
**A:** Offline handling approach:
```jsx
import NetInfo from '@react-native-community/netinfo';
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
setIsOnline(state.isConnected);
});
return () => unsubscribe();
}, []);
// Queue events when offline
const sendEventWithRetry = async (eventKey, data) => {
if (!isOnline) {
// Store in AsyncStorage for later retry
await storeEventForRetry(eventKey, data);
return;
}
try {
await embed.Event(eventKey, data);
} catch (error) {
// Retry logic or store for later
await storeEventForRetry(eventKey, data);
}
};
```
### 📞 Support
For additional help:
- **Documentation:** [https://docs.revrag.ai](https://docs.revrag.ai/)
- **Email Support:** [contact@revrag.ai](mailto:contact@revrag.ai)
- **GitHub Issues:** https://github.com/RevRag-ai/embed-react-native/issues
### 🔄 Migration Guide
If upgrading from a previous version, check the [CHANGELOG.md](https://www.notion.so/CHANGELOG.md) for breaking changes and migration steps.
---
**Last Updated:** January 2024
**SDK Version:** Latest
**React Native Compatibility:** 0.70+