> ## Documentation Index
> Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native SDK

> Official Telnyx React Native SDK for building cross-platform real-time communication applications with WebRTC.

# Telnyx React Native Voice SDK Demo

A comprehensive demo application showcasing the **@telnyx/react-voice-commons-sdk** library - a high-level, state-agnostic, drop-in module for the Telnyx React Native SDK that simplifies WebRTC voice calling integration.

## Overview

This demo app demonstrates how to integrate the `@telnyx/react-voice-commons-sdk` library to create a fully functional VoIP calling application with native call UI support, push notifications, and background handling.

### Key Features Demonstrated

* **TelnyxVoiceApp Integration**: Automatic lifecycle management and push notification handling
* **Native Call UI**: CallKit (iOS) and ConnectionService (Android) integration
* **Background Handling**: Seamless app state transitions and background call processing
* **Push Notifications**: Firebase (Android) and APNs (iOS) integration
* **Reactive State Management**: RxJS-based state streams for real-time UI updates
* **Modern UI Components**: Built with NativeWind v4 and react-native-reusables
* **Dark/Light Mode**: Persistent theme support with system navigation bar matching

## About @telnyx/react-voice-commons-sdk

The `@telnyx/react-voice-commons-sdk` library provides:

* **Headless Operation**: Core library that can establish and manage call state programmatically
* **Reactive Streams**: All state exposed via RxJS observables for easy integration
* **Simplified API**: Single facade class (`TelnyxVoipClient`) that hides underlying complexity
* **Automatic Lifecycle Management**: Background/foreground handling with `TelnyxVoiceApp` component
* **Call State Management**: Central state machine for managing multiple calls
* **Session Management**: Automatic connection lifecycle with reconnection logic
* **Push Notification Support**: Built-in handling for background push notifications
* **TypeScript Support**: Full TypeScript definitions for better developer experience

## Integration Guide

### Basic Setup

The demo shows how to integrate the library using the `TelnyxVoiceApp` component for automatic lifecycle management:

```tsx theme={null}
import { TelnyxVoiceApp, createTelnyxVoipClient } from '@telnyx/react-voice-commons-sdk';

// Create the VoIP client instance
const voipClient = createTelnyxVoipClient({
  enableAppStateManagement: true, // Optional: Enable automatic app state management (default: true)
  debug: true, // Optional: Enable debug logging
});

export default function App() {
  return (
    <TelnyxVoiceApp voipClient={voipClient} enableAutoReconnect={false} debug={true}>
      <YourAppContent />
    </TelnyxVoiceApp>
  );
}
```

## Core Components Used in Demo

### 1. VoIP Client Configuration

```tsx theme={null}
const voipClient = createTelnyxVoipClient({
  enableAppStateManagement: true, // Optional: Enable automatic app state management (default: true)
  debug: true, // Optional: Enable debug logging
});
```

**Configuration Options Explained:**

* **`enableAppStateManagement: true`** - **Optional (default: true)**: Enables automatic background/foreground app state management. When enabled, the library automatically disconnects when the app goes to background (unless there's an active call) and handles reconnection logic. Set to `false` if you want to handle app lifecycle manually.
* **`debug: true`** - **Optional**: Enables detailed logging for connection states, call transitions, and push notification processing. Useful for development and troubleshooting.

### 2. TelnyxVoiceApp Wrapper

The `TelnyxVoiceApp` component handles:

* Automatic background/foreground lifecycle management
* Push notification processing from terminated state
* Login state management with automatic reconnection
* Background client management for push notifications

### 3. Reactive State Management

```tsx theme={null}
// Listen to connection state
voipClient.connectionState$.subscribe((state) => {
  console.log('Connection state:', state);
});

// Listen to active calls
voipClient.calls$.subscribe((calls) => {
  console.log('Active calls:', calls);
});

// Handle individual call state
call.callState$.subscribe((state) => {
  console.log('Call state:', state);
});
```

### 4. Call Management

```tsx theme={null}
// Make a call
const call = await voipClient.newCall('1234567890');

// Answer incoming call
await call.answer();

// Control call
await call.mute();
await call.hold();
await call.hangup();

// Send DTMF tones (for IVRs, conference pins, etc.)
await call.dtmf('1'); // single digit
await call.dtmf('1234#'); // whole string
```

**DTMF:** `call.dtmf(digits)` sends each character as a Verto `INFO` message. Valid characters are `0-9`, `A-D`, `*`, and `#`; anything else is silently dropped. The call must be `ACTIVE` — throws otherwise.

### 5. Push Notification Flow (What Actually Happens)

You do not need to wire up JS push handlers. The native layer does the work:

* **Android**: `TelnyxFirebaseMessagingService` receives the FCM message, shows the incoming call notification, and (on Answer/Decline) launches `TelnyxMainActivity` with the call intent.
* **iOS**: `TelnyxVoipPushHandler` receives the PushKit payload and reports the call to CallKit.

In both cases the SDK connects the socket and restores the call internally — you just observe the VoIP client's streams to render UI.

**What to observe after the SDK-driven push login:**

* `voipClient.connectionState$` — emits `CONNECTED` when the socket is up and authenticated (there is no separate `loginState$`).
* `voipClient.activeCall$` — emits the `Call` once the SDK has processed the push and the call has arrived. Navigate to your in-call screen here.
* `voipClient.currentActiveCall` — synchronous accessor for the cold-start race: on a push-launched mount, the call may already be present by the time you subscribe, so check this first and route immediately if set.

```tsx theme={null}
React.useEffect(() => {
  if (voipClient.currentActiveCall) router.replace('/call');
  const sub = voipClient.activeCall$.subscribe((call) => {
    if (call) router.replace('/call');
  });
  return () => sub.unsubscribe();
}, []);
```

CallKit (iOS) and ConnectionService (Android) already render the native incoming-call UI, so this navigation only matters once the user taps into the app.

#### Detecting a Push-Launched Cold Start

When the OS wakes your app from a terminated state to deliver a call, the SDK is already logging in with stored credentials as part of the push flow. If your app *also* triggers a login on mount, you will get two competing sessions (double-login), which causes the call to fail or the socket to churn.

Use `TelnyxVoipClient.isLaunchedFromPushNotification()` to skip your own login when a push is in flight:

```tsx theme={null}
import { TelnyxVoipClient } from '@telnyx/react-voice-commons-sdk';

React.useEffect(() => {
  TelnyxVoipClient.isLaunchedFromPushNotification().then((isFromPush) => {
    if (isFromPush) {
      // SDK is handling login via the push flow — do nothing.
      return;
    }
    // Normal cold start — safe to auto-login.
    voipClient.loginFromStoredConfig();
  });
}, []);
```

This is a static method (callable before the client is constructed) and works on both platforms. It returns `true` if the app was launched by an FCM intent (Android) or a PushKit payload (iOS) that has not yet been consumed.

#### Common Mistake: Manual or Automatic Login on Push

The single most common integration bug is re-logging in while the SDK is already handling a push:

**Do not do this:**

```tsx theme={null}
// WRONG — runs on every mount, including push-launched cold starts
React.useEffect(() => {
  voipClient.login(config); // or loginFromStoredConfig(), or loginWithToken()
}, []);
```

**Do this instead:**

```tsx theme={null}
// Right — guard the login on push-launched cold starts
React.useEffect(() => {
  TelnyxVoipClient.isLaunchedFromPushNotification().then((isFromPush) => {
    if (!isFromPush) voipClient.loginFromStoredConfig();
  });
}, []);
```

Symptoms of getting this wrong: the incoming call rings briefly then disappears, the socket disconnects mid-call, or CallKit shows a call that immediately ends. If you see those, check whether your app is calling `login*` unconditionally on mount.

### Authentication & Persistent Storage

The library supports both credential-based and token-based authentication with automatic persistence for seamless reconnection.

### Authentication Methods

### 1. Credential-Based Authentication

```tsx theme={null}
import { createCredentialConfig } from '@telnyx/react-voice-commons-sdk';

const config = createCredentialConfig('your_sip_username', 'your_sip_password', {
  debug: true,
  pushNotificationDeviceToken: 'your_device_token',
});

await voipClient.login(config);
```

### 2. Token-Based Authentication

```tsx theme={null}
import { createTokenConfig } from '@telnyx/react-voice-commons-sdk';

const config = createTokenConfig('your_jwt_token', {
  debug: true,
  pushNotificationDeviceToken: 'your_device_token',
});

await voipClient.loginWithToken(config);
```

### Automatic Storage & Reconnection

The library automatically stores authentication data securely for seamless reconnection. **You don't need to manually manage these storage keys** - the library handles everything internally.

### Internal Storage (Managed Automatically)

The library uses these AsyncStorage keys internally:

* `@telnyx_username` - SIP username (credential auth)
* `@telnyx_password` - SIP password (credential auth)
* `@credential_token` - JWT authentication token (token auth)
* `@push_token` - Push notification device token

**Note**: These are managed automatically by the library. You only need to call `login()` once, and the library will handle storage and future reconnections.

### Auto-Reconnection

```tsx theme={null}
// Automatically reconnects using internally stored credentials or token
const success = await voipClient.loginFromStoredConfig();

if (!success) {
  // No stored authentication data found, show login UI
  console.log('Please log in again');
}
```

**When Auto-Reconnection Happens:**

* App launches from background/terminated state
* Push notification received while disconnected
* Network reconnection after connectivity loss
* App state changes (foreground/background transitions)

**Demo App Note**: The demo app's `TelnyxLoginForm` component does additional storage for UI convenience (pre-filling the login form). This is separate from the library's internal authentication storage and is not required for production apps.

### Manual Storage Management (Advanced Use Only)

If you need to clear stored authentication data manually:

```tsx theme={null}
import AsyncStorage from '@react-native-async-storage/async-storage';

// Clear all Telnyx authentication data
await AsyncStorage.multiRemove([
  '@telnyx_username',
  '@telnyx_password',
  '@credential_token',
  '@push_token',
]);
```

**Important**: This is typically not needed. The library manages authentication storage automatically. Only use this for advanced scenarios like implementing a "logout" feature that clears all stored data.

### Native Integration

The demo app shows complete native integration for both platforms. These integrations are required for production apps using the library.

### Android Integration

### 1. MainActivity Setup

Your app's MainActivity should extend `TelnyxMainActivity` for automatic push notification handling:

```kotlin theme={null}
// In your app's MainActivity.kt
import com.telnyx.react_voice_commons.TelnyxMainActivity

class MainActivity : TelnyxMainActivity() {
    // Your app-specific code

    override fun onHandleIntent(intent: Intent) {
        super.onHandleIntent(intent)
        // Handle any additional intent processing
    }
}
```

The `TelnyxMainActivity` provides:

* Automatic push notification intent handling
* Call action processing (Answer/Decline from notifications)
* Proper lifecycle management for VoIP functionality
* Integration with `VoicePnManager` for push notification state

### 2. Push Notification Setup

1. Place `google-services.json` in the project root.
2. Create an FCM service that extends `TelnyxFirebaseMessagingService` and a notification action receiver that extends `TelnyxNotificationActionReceiver`, then register both in `AndroidManifest.xml`. See [docs-markdown/push-notification/app-setup.md](./docs-markdown/push-notification/app-setup.md) for the full manifest + Kotlin boilerplate.

> **Do not** register `messaging().setBackgroundMessageHandler(...)` from JS. Android push is handled entirely by the native `TelnyxFirebaseMessagingService` — adding a JS handler will fight the native layer and can double-process calls. There is no `TelnyxVoiceApp.handleBackgroundPush` step required on Android.

### iOS Integration

### 1. AppDelegate Setup

Your AppDelegate only needs to implement `PKPushRegistryDelegate` for VoIP push notifications. CallKit integration is automatically handled by CallBridge:

```swift theme={null}
import PushKit
import TelnyxVoiceCommons

@UIApplicationMain
public class AppDelegate: ExpoAppDelegate, PKPushRegistryDelegate {

  public override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    // Your existing setup...

    // Initialize VoIP push registry via react-voice-commons
    TelnyxVoipPushHandler.initializeVoipRegistration()

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  // MARK: - VoIP Push Notifications
  public func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
    TelnyxVoipPushHandler.shared.handleVoipTokenUpdate(pushCredentials, type: type)
  }

  public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
    TelnyxVoipPushHandler.shared.handleVoipPush(payload, type: type, completion: completion)
  }
}
```

**Note**: CallKit integration (CXProvider, CXProviderDelegate, audio session management) is automatically handled by the internal CallBridge component. You don't need to implement any CallKit delegate methods manually.

### 2. VoIP Push Certificate Setup

* Configure VoIP push certificates in your Apple Developer account
* The `TelnyxVoipPushHandler` automatically handles token registration and push processing
* CallKit integration is automatically managed by CallBridge - no manual setup required

### Key Native Features Integrated

1. **Push Notification Handling**: Both platforms handle background push notifications properly
2. **Native Call UI**: CallKit (iOS, managed by CallBridge) and ConnectionService (Android) integration
3. **Audio Session Management**: Automatic audio session handling for VoIP calls via CallBridge
4. **Background Processing**: Seamless app state transitions and background call handling
5. **Intent Processing**: Android intent handling for notification actions (Answer/Decline)
6. **Token Management**: Automatic push token registration and updates

## Running the Demo Application

### Prerequisites

* Node.js and npm
* Expo CLI
* iOS development: Xcode
* Android development: Android Studio

### Installation and Setup

1. **Remove global Expo CLI** (if previously installed):

   ```bash theme={null}
   npm uninstall -g expo
   ```

2. **Install dependencies** (including local Expo):

   ```bash theme={null}
   npm install
   ```

3. **Run Expo install** to ensure all dependencies are properly configured:

   ```bash theme={null}
   npx expo install
   ```

4. **Configure Firebase for Android**:
   * Download the `google-services.json` file from your Firebase project
   * Place it in the root directory of the project

5. **Prebuild the project**:

   ```bash theme={null}
   npx expo prebuild
   ```

6. **Install iOS dependencies** (required for iOS):
   ```bash theme={null}
   cd ios && pod install && cd ..
   ```

### Running the Application

1. **Start Metro bundler** (in a separate terminal):

   ```bash theme={null}
   npx expo start --clear --reset-cache
   ```

2. **Run on iOS**:

   ```bash theme={null}
   npx expo run:ios
   ```

3. **Run on Android**:
   ```bash theme={null}
   npx expo run:android
   ```

> **Note**: Make sure you have the necessary development environment set up for iOS (Xcode) or Android (Android Studio) before running the respective commands.

### Demo App Features

Once running, the demo app provides:

* **Login Form**: Enter your Telnyx SIP credentials or token
* **Dialer Interface**: Make outgoing calls with a native dialer UI
* **Call Management**: Answer, decline, hold, mute, and transfer calls
* **Native Call UI**: Integrated CallKit (iOS) and ConnectionService (Android)
* **Push Notifications**: Receive calls when app is in background or terminated
* **Multiple Call Support**: Handle multiple simultaneous calls
* **Background Handling**: Seamless app state transitions

### Configuration

The demo app includes debug logging enabled by default:

```tsx theme={null}
// Enable debug logging for the Telnyx SDK
if (__DEV__) {
  log.setLevel('debug');
  (global as any).__TELNYX_DEBUG__ = true;
}
```

This provides detailed logging for:

* Connection state changes
* Call state transitions
* Push notification processing
* Background lifecycle events

## Troubleshooting

### iOS Connection Issues (First Run)

If you encounter network connection errors on iOS:

```bash theme={null}
# Try clearing cache and resetting
npx expo start --clear --reset-cache

# Or use tunnel mode
npx expo start --tunnel

# Or specify host explicitly
npx expo start --host lan
```

### iOS VoIP Push Notification Issues

If you see VoIP-related crashes on iOS, ensure your AppDelegate.swift includes the VoIP push notification delegate methods. The project includes these by default, but if you encounter issues, rebuild the iOS project:

```bash theme={null}
npx expo prebuild --platform ios --clean
npx expo run:ios
```

### Common Integration Issues

### Double Login

Two causes to check:

1. You're calling `login*` manually while `TelnyxVoiceApp` is running with `enableAutoReconnect={true}` — pick one.
2. You're calling `login*` on mount without guarding against push-launched cold starts. Wrap the call with `TelnyxVoipClient.isLaunchedFromPushNotification()` — see [Push Notification Flow](#5-push-notification-flow-what-actually-happens).

### Background Disconnection

Check if `enableAutoReconnect` is set appropriately for your use case in the `TelnyxVoiceApp` configuration.

### Push Notifications Not Working

* **Android**:
  * Verify `google-services.json` is in the correct location and Firebase is properly configured
  * Ensure your MainActivity extends `TelnyxMainActivity`
  * Check that `VoicePnManager` is properly handling push actions
* **iOS**:
  * Ensure VoIP push certificates are configured in your Apple Developer account
  * Verify AppDelegate implements `PKPushRegistryDelegate` and delegates to `TelnyxVoipPushHandler`
  * Check that `TelnyxVoipPushHandler.initializeVoipRegistration()` is called in `didFinishLaunchingWithOptions`
* **Both**: Verify the SDK is authenticated (or has stored credentials) — the native push flow will attempt to log in using stored credentials when a call comes in

### Native Integration Issues

* **Android**: Ensure MainActivity extends `TelnyxMainActivity` for proper intent handling
* **iOS**: Verify AppDelegate implements `PKPushRegistryDelegate` and delegates to `TelnyxVoipPushHandler`
* **CallKit**: On iOS, CallKit integration is automatically handled by CallBridge - no manual setup required

### Audio Issues

* **iOS**: Audio session management is automatically handled by CallBridge
* **Android**: Verify ConnectionService is properly configured for audio routing
* **Both**: Ensure proper audio permissions are granted

### Memory Leaks

Ensure you're unsubscribing from RxJS observables in your React components:

```tsx theme={null}
useEffect(() => {
  const subscription = voipClient.connectionState$.subscribe(handleStateChange);
  return () => subscription.unsubscribe();
}, []);
```

## Documentation

For complete API documentation and advanced usage patterns, see:

* **[API Reference](https://developers.telnyx.com/docs/development/webrtc/react-native-sdk)** - Auto-generated TypeDoc documentation
* **[TelnyxVoiceApp Documentation](https://developers.telnyx.com/docs/development/webrtc/react-native-sdk)** - Detailed component guide
* **[Trickle ICE Guide](./docs-markdown/call-features/trickle-ice.md)** - Enabling and disabling Trickle ICE for faster call setup

## License

MIT License - see LICENSE file for details.
