UNPKG

@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
# 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+