Skip to main content
Keep calls active when your app goes to the background. This requires platform-specific configuration to maintain audio/video streams and handle system interruptions.

iOS Configuration

Enable Background Modes

  1. Open your project in Xcode
  2. Select your target and go to Signing & Capabilities
  3. Add Background Modes capability
  4. Enable:
    • Audio, AirPlay, and Picture in Picture
    • Voice over IP (for VoIP push notifications)

Configure Audio Session

The SDK automatically configures the audio session, but you can customize it in your native code:
// ios/AppDelegate.swift
import AVFoundation

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Configure audio session for calls
    do {
        try AVAudioSession.sharedInstance().setCategory(
            .playAndRecord,
            mode: .voiceChat,
            options: [.allowBluetooth, .allowBluetoothA2DP, .defaultToSpeaker]
        )
        try AVAudioSession.sharedInstance().setActive(true)
    } catch {
        print("Failed to configure audio session: \(error)")
    }
    
    return true
}

Android Configuration

Add Permissions

Add to your AndroidManifest.xml:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

Configure Foreground Service

For Android 10+, calls require a foreground service to continue in the background:
<service
    android:name="com.cometchat.calls.CallForegroundService"
    android:foregroundServiceType="phoneCall"
    android:exported="false" />

Keep Screen Awake

The SDK automatically manages wake locks during calls. No additional configuration is needed.

Handle App State Changes

Monitor app state to handle background transitions:
import { useEffect, useRef } from 'react';
import { AppState, AppStateStatus } from 'react-native';
import { CometChatCalls } from '@cometchat/calls-sdk-react-native';

function useBackgroundHandling() {
  const appState = useRef(AppState.currentState);

  useEffect(() => {
    const subscription = AppState.addEventListener(
      'change',
      (nextAppState: AppStateStatus) => {
        if (
          appState.current.match(/active/) &&
          nextAppState === 'background'
        ) {
          console.log('App going to background');
          // Optionally enable PiP
          CometChatCalls.enablePictureInPictureLayout();
        } else if (
          appState.current === 'background' &&
          nextAppState === 'active'
        ) {
          console.log('App coming to foreground');
          // Optionally disable PiP
          CometChatCalls.disablePictureInPictureLayout();
        }
        appState.current = nextAppState;
      }
    );

    return () => {
      subscription.remove();
    };
  }, []);
}

export default useBackgroundHandling;

Handle Audio Interruptions

Handle system audio interruptions (phone calls, alarms, etc.):
import { useEffect } from 'react';
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';

function useAudioInterruptions() {
  useEffect(() => {
    if (Platform.OS === 'ios') {
      // iOS handles audio interruptions automatically
      // The SDK will pause/resume as needed
      return;
    }

    // Android: Listen for audio focus changes
    // This is typically handled by the SDK automatically
  }, []);
}

export default useAudioInterruptions;

Connection Events

Listen for connection state changes:
import { CometChatCalls } from '@cometchat/calls-sdk-react-native';

// Connection lost (e.g., network issues)
CometChatCalls.addEventListener('onConnectionLost', () => {
  console.log('Connection lost - attempting to reconnect');
});

// Connection restored
CometChatCalls.addEventListener('onConnectionRestored', () => {
  console.log('Connection restored');
});

// Connection closed
CometChatCalls.addEventListener('onConnectionClosed', () => {
  console.log('Connection closed');
});

Complete Example

import React, { useEffect, useRef, useCallback } from 'react';
import { View, StyleSheet, AppState, AppStateStatus } from 'react-native';
import { CometChatCalls } from '@cometchat/calls-sdk-react-native';

interface CallScreenProps {
  callToken: string;
  callSettings: any;
  onCallEnd: () => void;
}

function CallScreen({ callToken, callSettings, onCallEnd }: CallScreenProps) {
  const appState = useRef(AppState.currentState);
  const isInCall = useRef(true);

  // Handle app state changes
  useEffect(() => {
    const subscription = AppState.addEventListener(
      'change',
      (nextAppState: AppStateStatus) => {
        if (!isInCall.current) return;

        if (
          appState.current.match(/active/) &&
          nextAppState === 'background'
        ) {
          // Going to background - enable PiP for video calls
          CometChatCalls.enablePictureInPictureLayout();
        } else if (
          appState.current === 'background' &&
          nextAppState === 'active'
        ) {
          // Coming to foreground - disable PiP
          CometChatCalls.disablePictureInPictureLayout();
        }
        appState.current = nextAppState;
      }
    );

    return () => {
      subscription.remove();
    };
  }, []);

  // Handle connection events
  useEffect(() => {
    const unsubscribeLost = CometChatCalls.addEventListener(
      'onConnectionLost',
      () => {
        console.log('Connection lost');
        // Show reconnecting UI
      }
    );

    const unsubscribeRestored = CometChatCalls.addEventListener(
      'onConnectionRestored',
      () => {
        console.log('Connection restored');
        // Hide reconnecting UI
      }
    );

    const unsubscribeClosed = CometChatCalls.addEventListener(
      'onConnectionClosed',
      () => {
        console.log('Connection closed');
        isInCall.current = false;
        onCallEnd();
      }
    );

    return () => {
      unsubscribeLost();
      unsubscribeRestored();
      unsubscribeClosed();
    };
  }, [onCallEnd]);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      isInCall.current = false;
    };
  }, []);

  return (
    <View style={styles.container}>
      <CometChatCalls.Component
        callToken={callToken}
        callSettings={callSettings}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#000',
  },
});

export default CallScreen;

Platform Behavior

iOS

ScenarioBehavior
App backgroundedAudio continues, video pauses
Phone call receivedCall audio is interrupted
Phone call endedCall audio resumes
Screen lockedAudio continues

Android

ScenarioBehavior
App backgroundedAudio continues with foreground service
Phone call receivedCall audio may be interrupted
Screen offAudio continues with wake lock