Primeros Pasos
Instalación
Section titled “Instalación”npm install @capgo/capacitor-twilio-voicenpx cap syncyarn add @capgo/capacitor-twilio-voicenpx cap syncpnpm add @capgo/capacitor-twilio-voicenpx cap syncbun add @capgo/capacitor-twilio-voicenpx cap syncPrerrequisitos
Section titled “Prerrequisitos”Necesitarás una cuenta de Twilio y tokens de acceso para la autenticación. Regístrate en Twilio si no tienes una cuenta.
Configuración de la Plataforma
Section titled “Configuración de la Plataforma”Agrega los permisos requeridos a tu Info.plist:
<key>NSMicrophoneUsageDescription</key><string>Esta aplicación necesita acceso al micrófono para llamadas de voz</string>Para llamadas entrantes con PushKit:
- Habilita las Notificaciones Push en las capacidades de Xcode
- Agrega el modo de fondo VoIP
- Configura el certificado VoIP en la consola de Twilio
Android
Section titled “Android”Agrega los permisos requeridos a tu AndroidManifest.xml:
<uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /><uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />Configura Firebase para notificaciones push:
- Agrega
google-services.jsona tu proyecto Android - Configura FCM en la consola de Twilio
Ejemplo de Uso
Section titled “Ejemplo de Uso”import { TwilioVoice } from '@capgo/capacitor-twilio-voice';
// Autenticarse con Twilioawait TwilioVoice.login({ accessToken: 'tu-token-de-acceso-twilio'});
// Hacer una llamadaawait TwilioVoice.makeCall({ to: '+1234567890'});
// Escuchar eventos de llamadaTwilioVoice.addListener('callConnected', (data) => { console.log('Llamada conectada:', data.callSid);});
TwilioVoice.addListener('callDisconnected', (data) => { console.log('Llamada desconectada:', data.callSid);});
TwilioVoice.addListener('incomingCall', (data) => { console.log('Llamada entrante de:', data.from);
// Aceptar la llamada TwilioVoice.acceptCall({ callSid: data.callSid });
// O rechazarla // TwilioVoice.rejectCall({ callSid: data.callSid });});
// Silenciar/activar llamadaawait TwilioVoice.muteCall({ muted: true, callSid: 'sid-de-llamada-actual'});
// Activar altavozawait TwilioVoice.setSpeaker({ enabled: true});
// Finalizar llamadaawait TwilioVoice.endCall({ callSid: 'sid-de-llamada-actual'});
// Cerrar sesiónawait TwilioVoice.logout();Referencia de API
Section titled “Referencia de API”login(options)
Section titled “login(options)”login(options: { accessToken: string }) => Promise<void>Autenticarse con Twilio usando un token de acceso.
| Parámetro | Tipo |
|---|---|
options | { accessToken: string } |
logout()
Section titled “logout()”logout() => Promise<void>Finalizar la sesión del usuario y limpiar el estado de llamadas.
isLoggedIn()
Section titled “isLoggedIn()”isLoggedIn() => Promise<{ loggedIn: boolean }>Verificar el estado de autenticación actual.
Retorna: Promise<{ loggedIn: boolean }>
makeCall(options)
Section titled “makeCall(options)”makeCall(options: { to: string; params?: Record<string, string> }) => Promise<{ callSid: string }>Iniciar una llamada saliente a un número específico.
| Parámetro | Tipo |
|---|---|
options | { to: string; params?: Record<string, string> } |
Retorna: Promise<{ callSid: string }>
acceptCall(options)
Section titled “acceptCall(options)”acceptCall(options: { callSid: string }) => Promise<void>Aceptar una llamada entrante.
| Parámetro | Tipo |
|---|---|
options | { callSid: string } |
rejectCall(options)
Section titled “rejectCall(options)”rejectCall(options: { callSid: string }) => Promise<void>Rechazar una llamada entrante.
| Parámetro | Tipo |
|---|---|
options | { callSid: string } |
endCall(options?)
Section titled “endCall(options?)”endCall(options?: { callSid?: string }) => Promise<void>Terminar una llamada activa.
| Parámetro | Tipo |
|---|---|
options | { callSid?: string } (opcional) |
muteCall(options)
Section titled “muteCall(options)”muteCall(options: { muted: boolean; callSid?: string }) => Promise<void>Silenciar o activar el audio de la llamada.
| Parámetro | Tipo |
|---|---|
options | { muted: boolean; callSid?: string } |
setSpeaker(options)
Section titled “setSpeaker(options)”setSpeaker(options: { enabled: boolean }) => Promise<void>Activar o desactivar el altavoz.
| Parámetro | Tipo |
|---|---|
options | { enabled: boolean } |
sendDigits(options)
Section titled “sendDigits(options)”sendDigits(options: { digits: string; callSid?: string }) => Promise<void>Enviar tonos DTMF durante una llamada.
| Parámetro | Tipo |
|---|---|
options | { digits: string; callSid?: string } |
Listeners de Eventos
Section titled “Listeners de Eventos”Eventos Disponibles
Section titled “Eventos Disponibles”registered- Registrado exitosamente con Twiliounregistered- Desregistrado de TwilioregistrationFailed- Fallo en el registroincomingCall- Llamada entrante recibidacallConnected- Llamada conectada exitosamentecallDisconnected- Llamada desconectadacallRinging- Llamada saliente sonandocallReconnecting- Llamada reconectandocallReconnected- Llamada reconectada después de interrupciónqualityWarning- Advertencia de calidad de llamadaerror- Ocurrió un error
Ejemplos de Eventos
Section titled “Ejemplos de Eventos”// Manejar llamadas entrantesTwilioVoice.addListener('incomingCall', (data) => { console.log('Llamada entrante de:', data.from); console.log('Call SID:', data.callSid);
// Mostrar UI de llamada entrante showIncomingCallScreen({ from: data.from, callSid: data.callSid });});
// Manejar cambios de estado de llamadaTwilioVoice.addListener('callConnected', (data) => { console.log('Llamada conectada:', data.callSid); startCallTimer();});
TwilioVoice.addListener('callDisconnected', (data) => { console.log('Llamada finalizada:', data.callSid); stopCallTimer(); hideCallScreen();});
// Manejar advertencias de calidadTwilioVoice.addListener('qualityWarning', (data) => { console.warn('Advertencia de calidad de llamada:', data.warning); showQualityWarning(data.warning);});
// Manejar erroresTwilioVoice.addListener('error', (error) => { console.error('Error de Twilio:', error.message); handleError(error);});
// Remover listeners cuando terminesconst listener = await TwilioVoice.addListener('callConnected', (data) => { console.log('Conectado');});
// Más tarde...listener.remove();Ejemplo Completo
Section titled “Ejemplo Completo”import { TwilioVoice } from '@capgo/capacitor-twilio-voice';
class VoiceCallService { private currentCallSid: string | null = null; private isMuted = false; private isSpeakerOn = false;
async initialize(accessToken: string) { try { // Iniciar sesión en Twilio await TwilioVoice.login({ accessToken });
// Verificar estado de inicio de sesión const { loggedIn } = await TwilioVoice.isLoggedIn(); console.log('Estado de inicio de sesión:', loggedIn);
// Configurar listeners de eventos this.setupEventListeners();
} catch (error) { console.error('Fallo al inicializar:', error); } }
setupEventListeners() { // Eventos de registro TwilioVoice.addListener('registered', () => { console.log('Registrado exitosamente con Twilio'); });
TwilioVoice.addListener('registrationFailed', (error) => { console.error('Fallo en el registro:', error); });
// Llamada entrante TwilioVoice.addListener('incomingCall', async (data) => { console.log('Llamada entrante de:', data.from);
const accepted = await this.showIncomingCallDialog(data.from);
if (accepted) { await TwilioVoice.acceptCall({ callSid: data.callSid }); this.currentCallSid = data.callSid; } else { await TwilioVoice.rejectCall({ callSid: data.callSid }); } });
// Eventos de llamada TwilioVoice.addListener('callConnected', (data) => { console.log('Llamada conectada'); this.currentCallSid = data.callSid; this.showCallScreen(); });
TwilioVoice.addListener('callDisconnected', () => { console.log('Llamada desconectada'); this.currentCallSid = null; this.hideCallScreen(); });
TwilioVoice.addListener('callRinging', () => { console.log('Llamada sonando...'); });
// Advertencias de calidad TwilioVoice.addListener('qualityWarning', (data) => { console.warn('Advertencia de calidad de llamada:', data.warning); this.showQualityIndicator(data.warning); }); }
async makeCall(phoneNumber: string) { try { const result = await TwilioVoice.makeCall({ to: phoneNumber, params: { // Parámetros personalizados opcionales customerId: 'customer-123' } });
this.currentCallSid = result.callSid; console.log('Llamada iniciada:', result.callSid); } catch (error) { console.error('Fallo al hacer llamada:', error); } }
async endCall() { if (this.currentCallSid) { await TwilioVoice.endCall({ callSid: this.currentCallSid }); this.currentCallSid = null; } }
async toggleMute() { this.isMuted = !this.isMuted; await TwilioVoice.muteCall({ muted: this.isMuted, callSid: this.currentCallSid || undefined }); }
async toggleSpeaker() { this.isSpeakerOn = !this.isSpeakerOn; await TwilioVoice.setSpeaker({ enabled: this.isSpeakerOn }); }
async sendDTMF(digits: string) { if (this.currentCallSid) { await TwilioVoice.sendDigits({ digits, callSid: this.currentCallSid }); } }
async logout() { await TwilioVoice.logout(); }
private async showIncomingCallDialog(from: string): Promise<boolean> { // Mostrar diálogo nativo o UI personalizada return confirm(`Llamada entrante de ${from}`); }
private showCallScreen() { // Mostrar UI de llamada console.log('Mostrando pantalla de llamada'); }
private hideCallScreen() { // Ocultar UI de llamada console.log('Ocultando pantalla de llamada'); }
private showQualityIndicator(warning: string) { // Mostrar advertencia de calidad console.log('Advertencia de calidad:', warning); }}
// Usoconst voiceService = new VoiceCallService();
// Inicializar con token de acceso desde tu backendconst token = await fetchTwilioToken();await voiceService.initialize(token);
// Hacer una llamadaawait voiceService.makeCall('+1234567890');
// Controlar llamadaawait voiceService.toggleMute();await voiceService.toggleSpeaker();await voiceService.sendDTMF('1');
// Finalizar llamadaawait voiceService.endCall();
// Cerrar sesiónawait voiceService.logout();Mejores Prácticas
Section titled “Mejores Prácticas”- Obtén tokens de acceso desde tu servidor backend, nunca los incluyas en la aplicación
- Implementa lógica de renovación de tokens para sesiones de larga duración
- Maneja interrupciones de red con lógica de reconexión
- Proporciona retroalimentación visual para los estados de llamada
- Prueba en dispositivos reales con notificaciones push
- Implementa manejo de errores apropiado
- Limpia los listeners cuando los componentes se desmonten
- Solicita permisos de micrófono antes de hacer llamadas
Consideraciones de Seguridad
Section titled “Consideraciones de Seguridad”- Nunca almacenes credenciales de Twilio en la aplicación
- Genera tokens de acceso en tu backend
- Implementa expiración y renovación de tokens
- Usa HTTPS para todas las solicitudes de tokens
- Valida las llamadas entrantes del lado del servidor
Solución de Problemas
Section titled “Solución de Problemas”Las Llamadas no se Conectan
Section titled “Las Llamadas no se Conectan”- Verifica que el token de acceso sea válido y no haya expirado
- Verifica la conectividad de red
- Asegúrate de que los permisos de micrófono estén otorgados
- Verifica que la cuenta de Twilio esté configurada correctamente
Las Notificaciones Push no Funcionan
Section titled “Las Notificaciones Push no Funcionan”- Verifica que los certificados de PushKit/FCM estén configurados en Twilio
- Verifica que el dispositivo esté registrado para notificaciones push
- Prueba con certificados de producción
Problemas de Audio
Section titled “Problemas de Audio”- Verifica los permisos de micrófono
- Verifica la configuración de altavoz/bluetooth
- Prueba el enrutamiento de audio en dispositivos reales
Visualización del Nombre del Llamante (CapacitorTwilioCallerName)
Section titled “Visualización del Nombre del Llamante (CapacitorTwilioCallerName)”Por defecto, las llamadas entrantes muestran el número de teléfono del llamante o el ID del cliente. Puedes personalizar esto pasando un parámetro CapacitorTwilioCallerName desde tu backend TwiML para mostrar un nombre amigable en su lugar.
Configuración del Backend
Section titled “Configuración del Backend”Al generar tu respuesta TwiML para la marcación <Client>, agrega el parámetro CapacitorTwilioCallerName:
// Ejemplo en JavaParameter callerNameParam = new Parameter.Builder() .name("CapacitorTwilioCallerName") .value("John Doe") .build();
Client client = new Client.Builder(identity) .parameter(callerNameParam) .build();
Dial dial = new Dial.Builder() .client(client) .build();// Ejemplo en Node.jsconst VoiceResponse = require('twilio').twiml.VoiceResponse;
const response = new VoiceResponse();const dial = response.dial();dial.client({ name: 'CapacitorTwilioCallerName', value: 'John Doe'}, identity);Cómo Funciona
Section titled “Cómo Funciona”- Cuando tu backend recibe una llamada entrante, genera TwiML para enrutar la llamada
- Incluye el parámetro
CapacitorTwilioCallerNamecon el nombre de visualización del llamante - El plugin extrae automáticamente este parámetro y lo usa para:
- Pantalla de llamada entrante de CallKit en iOS
- Notificación de llamada entrante en Android
- El campo
fromen eventosincomingCall - El array
pendingInvitesen el estado de llamada
Si CapacitorTwilioCallerName no se proporciona, el plugin recurre al número de teléfono del llamante o al ID del cliente.