Consentire agli utenti di effettuare chiamate verso numeri di telefono reali fisici (PSTN) direttamente da un'applicazione iOS senza passare per le tariffe tradizionali degli operatori cellulari richiede un'architettura **WebRTC to PSTN bridge**. Piattaforme come Telnyx mettono a disposizione SDK nativi per iOS (TelnyxRTC) che trasformano lo smartphone in uno snodo telefonico IP.
1. Generazione delle Credenziali JWT Server-Side
Per ragioni di sicurezza, le API Key del provider di telefonia non devono mai risiedere nel codice dell'app iOS. L'applicazione richiede un token di sessione temporaneo ad una Cloud Function serverless:
import { onCall, HttpsError } from "firebase-functions/v2/https";
import axios from "axios";
export const getVoiceSdkToken = onCall(async (request) => {
if (!request.auth) {
throw new HttpsError("unauthenticated", "Utente non autenticato.");
}
const connectionId = process.env.TELNYX_VOICE_SDK_CONNECTION_ID;
const apiKey = process.env.TELNYX_API_KEY;
try {
const response = await axios.post(
`https://api.telnyx.com/v2/telephony_credentials/${connectionId}/token`,
{ ttl_seconds: 3600 },
{ headers: { Authorization: `Bearer ${apiKey}` } }
);
return { token: response.data };
} catch (error: any) {
throw new HttpsError("internal", "Errore generazione token VoIP.");
}
});
2. Inizializzazione di TelnyxClient ed AVAudioSession in Swift
In Swift configuriamo l'istanza client WebRTC gestendo la categoria audio per la voce bidirezionale (.playAndRecord):
import SwiftUI
import AVFoundation
import TelnyxRTC
class VoiceCallManager: ObservableObject, TxClientDelegate {
@Published var callState: String = "Inattivo"
private var client: TxClient?
private var currentCall: Call?
func initializeVoIPSession(jwtToken: String) {
// Configurazione della sessione audio per la fonia
let audioSession = AVAudioSession.sharedInstance()
try? audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .allowBluetoothA2DP])
try? audioSession.setActive(true)
// Inizializzazione SDK WebRTC
client = TxClient()
client?.delegate = self
client?.connect(token: jwtToken)
}
func makeCall(destinationE164: String) {
guard let client = client else { return }
currentCall = client.newCall(
callerName: "AfriCall User",
callerNumber: destinationE164,
destinationNumber: destinationE164
)
currentCall?.call()
self.callState = "Chiamata in corso verso \(destinationE164)..."
}
func endCall() {
currentCall?.hangup()
self.callState = "Terminata"
}
// TxClientDelegate Callback
func onClientConnected() {
print("Connessione WebRTC VoIP stabilita con il gateway PSTN.")
}
}
3. Dialer Numpad in SwiftUI
Con SwiftUI possiamo costruire un tastierino telefonico elegante che compone il numero in formato standard E.164 (es. +393401234567):
struct DialerPadView: View {
@StateObject private var voip = VoiceCallManager()
@State private var phoneNumber: String = "+"
var body: some View {
VStack(spacing: 20) {
Text(phoneNumber)
.font(.largeTitle)
.bold()
.padding()
Text(voip.callState)
.font(.subheadline)
.foregroundColor(.gray)
HStack {
Button(action: { voip.makeCall(destinationE164: phoneNumber) }) {
Image(systemName: "phone.fill")
.font(.title)
.padding()
.background(Color.green)
.foregroundColor(.white)
.clipShape(Circle())
}
Button(action: { voip.endCall() }) {
Image(systemName: "phone.down.fill")
.font(.title)
.padding()
.background(Color.red)
.foregroundColor(.white)
.clipShape(Circle())
}
}
}
}
}
Conclusione
Il bridging audio WebRTC verso la rete PSTN apre scenari straordinari per la telefonia aziendale ed internazionale. Per consulenze sullo sviluppo di app di telefonia VoIP ed integrazioni Telnyx su iOS, scrivi a diallooyunus@gmail.com.