Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WEBRTC-2470: [IoS] Document Update #166

Merged
merged 7 commits into from
Jan 31, 2025
57 changes: 46 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Enable Telnyx real-time communication services on iOS. :telephone_receiver: :fir
## Project Setup:

1. Clone the repository
2. Run the command `pod install` to install de dependencies inside the project root folder.
2. Run the command `pod install` to install the dependencies inside the project root folder.
3. Open the Workspace : `TelnyxRTC.xcworkspace`
4. You will find 3 targets to build:
- The SDK
Expand All @@ -34,7 +34,7 @@ Enable Telnyx real-time communication services on iOS. :telephone_receiver: :fir
<img width="153" align="center" alt="Screen Shot 2021-03-03 at 10 04 05" src="https://user-images.githubusercontent.com/75636882/109810077-d4b24400-7c07-11eb-91ec-d81e72ae9069.png">
</p>

7. Select target `TelnyxWebRTCDemo` to run the demo app. The SDK should be manually builded in order to get the app running (Step 5)
7. Select target `TelnyxWebRTCDemo` to run the demo app. The SDK should be manually built in order to get the app running (Step 5)

8. Enjoy 😎
</br>
Expand Down Expand Up @@ -166,7 +166,7 @@ do {
}

// You can call client.disconnect() when you're done.
Note: you need to relese the delegate manually when you are done.
Note: you need to release the delegate manually when you are done.

// Disconnecting and Removing listeners.
telnyxClient.disconnect();
Expand Down Expand Up @@ -310,6 +310,49 @@ This is a general example: In order to fully support inbound calls you will need
---
</br>

## WebRTC Statistics

The SDK provides WebRTC statistics functionality to assist with troubleshooting and monitoring call quality. This feature is controlled through the `debug` flag in the `TxClient` configuration.

### Enabling WebRTC Statistics

To enable WebRTC statistics logging:

```Swift
let txConfig = TxConfig(sipUser: sipUser,
password: password,
pushDeviceToken: "DEVICE_APNS_TOKEN",
debug: true) // Enable WebRTC statistics
```

### Understanding WebRTC Statistics

When `debug: true` is configured:
- WebRTC statistics logs are automatically collected during calls
- Logs are sent to the Telnyx portal and are accessible in the Object Storage section
- Statistics are linked to the SIP credential used for testing
- The logs help the Telnyx support team diagnose issues and optimize call quality

### Important Notes

1. **Log Access**:
- If you run the app using SIP credential A with `debug: true`, the WebRTC logs will be available in the Telnyx portal account associated with credential A
- Logs are stored in the Object Storage section of your Telnyx portal

2. **Troubleshooting Support**:
- WebRTC statistics are primarily intended to assist the Telnyx support team
- When requesting support, enable `debug: true` in `TxClient` for all instances
- Provide the `debug ID` or `callId` when contacting support
- Statistics logging is disabled by default to optimize performance

3. **Best Practices**:
- Enable `debug: true` only when troubleshooting is needed
- Remember to provide the `debug ID` or `callId` in support requests
- Consider disabling debug mode in production unless actively investigating issues

---
</br>

## Setting up VoIP push notifications:

In order to receive incoming calls while the app is running in background or closed, you will need to perform a set of configurations over your Mission Control Portal Account and your application.
Expand Down Expand Up @@ -726,14 +769,6 @@ Note : Signing back in, using same credentials will re-enable push notifications
### Privacy Manifest
Support for privacy manifest is added from version 0.1.26

### Sending Debug Stats
In case of any need to investigate any issue by Telnyx, please enable the debug stats that will be sent to Telnyx for analysis.

```Swift
currentCall?.startDebugStats()
```
Please store the **debug_stats_id** that would be used for investigation

### Documentation:
For more information you can:
1. Clone the repository
Expand Down
35 changes: 20 additions & 15 deletions TelnyxRTC/Telnyx/Models/TxConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,24 @@ public struct TxConfig {
public internal(set) var reconnectClient: Bool = true
public internal(set) var pushEnvironment: PushEnvironment?

// To enable stats debuggin
/// Enables WebRTC communication statistics reporting to Telnyx servers.
/// - Note: This flag is different from `logLevel`:
/// - `debug`: When enabled, sends WebRTC communication statistics to Telnyx servers for monitoring and debugging purposes.
/// See `WebRTCStatsReporter` class for details on the statistics collected.
/// - `logLevel`: Controls console log output in Xcode when running the app in debug mode.
/// - Important: The `debug` flag is disabled by default to minimize data usage.
public internal(set) var debug: Bool = false

// MARK: - Initializers

/// Constructor of the Telnyx SDK configuration: Login using sip user and password.
/// Constructor for the Telnyx SDK configuration using SIP credentials.
/// - Parameters:
/// - sipUser: sipUser the SIP user
/// - password: password the password of the SIP user.
/// - pushDeviceToken: (Optional) the device push notification token. This is required to receive Inbound calls notifications.
/// - ringtone: (Optional) The audio file name to be played when receiving an incoming call. e.g.: "my-ringtone.mp3"
/// - ringBackTone: (Optional) The audio file to be played when calling. e.g.: "my-ringbacktone.mp3"
/// - logLevel: (Optional) Can select the verbosity level of the SDK logs. Is set to `.none` as default
/// - sipUser: The SIP username for authentication
/// - password: The password associated with the SIP user
/// - pushDeviceToken: (Optional) The device's push notification token, required for receiving inbound call notifications
/// - ringtone: (Optional) The audio file name to play for incoming calls (e.g., "my-ringtone.mp3")
/// - ringBackTone: (Optional) The audio file name to play while making outbound calls (e.g., "my-ringbacktone.mp3")
/// - logLevel: (Optional) The verbosity level for SDK logs (defaults to `.none`)
public init(sipUser: String, password: String,
pushDeviceToken: String? = nil,
ringtone: String? = nil,
Expand All @@ -64,14 +69,14 @@ public struct TxConfig {
Logger.log.verboseLevel = logLevel
}

/// Constructor of the Telnyx SDK configuration: Login using a token.
/// Constructor for the Telnyx SDK configuration using JWT token authentication.
/// - Parameters:
/// - token: Token generated from https://developers.telnyx.com/docs/v2/webrtc/quickstart
/// - pushDeviceToken: (Optional) the device push notification token. This is required to receive Inbound calls notifications.
/// - ringtone: (Optional) The audio file name to be played when receiving an incoming call. e.g.: "my-ringtone.mp3"
/// - ringBackTone: (Optional) The audio file name to be played when calling. e.g.: "my-ringbacktone.mp3"
/// - logLevel: (Optional) Can select the verbosity level of the SDK logs. Is set to `.none` as default
/// - serverConfiguration: (Optional) To define a custom `signaling server` and `TURN/ STUN servers`. As default we use the internal Telnyx Production servers.
/// - token: JWT token generated from https://developers.telnyx.com/docs/v2/webrtc/quickstart
/// - pushDeviceToken: (Optional) The device's push notification token, required for receiving inbound call notifications
/// - ringtone: (Optional) The audio file name to play for incoming calls (e.g., "my-ringtone.mp3")
/// - ringBackTone: (Optional) The audio file name to play while making outbound calls (e.g., "my-ringbacktone.mp3")
/// - logLevel: (Optional) The verbosity level for SDK logs (defaults to `.none`)
/// - serverConfiguration: (Optional) Custom configuration for signaling server and TURN/STUN servers (defaults to Telnyx Production servers)
public init(token: String,
pushDeviceToken: String? = nil,
ringtone: String? = nil,
Expand Down
87 changes: 78 additions & 9 deletions TelnyxRTC/Telnyx/TxClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,25 @@ public class TxClient {
}
}

/// When implementing CallKit framework, audio has to be manually handled.
/// Set this property to TRUE when `provider(CXProvider, didActivate: AVAudioSession)` is called on your CallKit implementation
/// Set this property to FALSE when `provider(CXProvider, didDeactivate: AVAudioSession)` is called on your CallKit implementation
/// Controls the audio device state when using CallKit integration.
/// This property manages the WebRTC audio session activation and deactivation.
///
/// When implementing CallKit, you must manually handle the audio session state:
/// - Set to `true` in `provider(_:didActivate:)` to enable audio
/// - Set to `false` in `provider(_:didDeactivate:)` to disable audio
///
/// Example usage with CallKit:
/// ```swift
/// extension CallKitProvider: CXProviderDelegate {
/// func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
/// telnyxClient.isAudioDeviceEnabled = true
/// }
///
/// func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
/// telnyxClient.isAudioDeviceEnabled = false
/// }
/// }
/// ```
public var isAudioDeviceEnabled : Bool {
get {
return RTCAudioSession.sharedInstance().isAudioEnabled
Expand All @@ -178,17 +194,46 @@ public class TxClient {
}
}


public func enableAudioSession(audioSession: AVAudioSession){
/// Enables and configures the audio session for a call.
/// This method sets up the appropriate audio configuration and activates the session.
///
/// - Parameter audioSession: The AVAudioSession instance to configure
/// - Important: This method MUST be called from the CXProviderDelegate's `provider(_:didActivate:)` callback
/// to properly handle audio routing when using CallKit integration.
///
/// Example usage:
/// ```swift
/// func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
/// print("provider:didActivateAudioSession:")
/// self.telnyxClient.enableAudioSession(audioSession: audioSession)
/// }
/// ```
public func enableAudioSession(audioSession: AVAudioSession) {
setupCorrectAudioConfiguration()
setAudioSessionActive(true)
}

public func disableAudioSession(audioSession: AVAudioSession){
/// Disables and resets the audio session.
/// This method cleans up the audio configuration and deactivates the session.
///
/// - Parameter audioSession: The AVAudioSession instance to reset
/// - Important: This method MUST be called from the CXProviderDelegate's `provider(_:didDeactivate:)` callback
/// to properly clean up audio resources when using CallKit integration.
///
/// Example usage:
/// ```swift
/// func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
/// print("provider:didDeactivateAudioSession:")
/// self.telnyxClient.disableAudioSession(audioSession: audioSession)
/// }
/// ```
public func disableAudioSession(audioSession: AVAudioSession) {
resetAudioConfiguration()
setAudioSessionActive(false)
}

/// The current audio route configuration.
/// This provides information about the active input and output ports.
let currentRoute = AVAudioSession.sharedInstance().currentRoute

/// Client must be registered in order to receive or place calls.
Expand All @@ -208,6 +253,13 @@ public class TxClient {
setupAudioRouteChangeMonitoring()
}

/// Sets up monitoring for audio route changes (e.g., headphones connected/disconnected,
/// Bluetooth device connected/disconnected).
///
/// This method registers for AVAudioSession route change notifications to:
/// - Track when audio devices are connected or disconnected
/// - Monitor changes in the active audio output
/// - Update the speaker state accordingly
private func setupAudioRouteChangeMonitoring() {
NotificationCenter.default.addObserver(
self,
Expand All @@ -216,6 +268,23 @@ public class TxClient {
object: nil)
}

/// Handles audio route change notifications from the system.
///
/// This method processes audio route changes and:
/// - Updates the internal speaker state
/// - Notifies observers about audio route changes
/// - Manages audio routing between available outputs
///
/// The method posts an "AudioRouteChanged" notification with:
/// - isSpeakerEnabled: Whether the built-in speaker is active
/// - outputPortType: The type of the current audio output port
///
/// Common route change reasons handled:
/// - .categoryChange: Audio session category was changed
/// - .override: Route was overridden by the system or user
/// - .routeConfigurationChange: Available routes were changed
///
/// @objc attribute is required for NotificationCenter selector
@objc private func handleAudioRouteChange(notification: Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
Expand All @@ -226,7 +295,7 @@ public class TxClient {
let session = AVAudioSession.sharedInstance()
let currentRoute = session.currentRoute

// Check if we have any output ports
// Ensure we have at least one output port
guard let output = currentRoute.outputs.first else {
return
}
Expand All @@ -235,11 +304,11 @@ public class TxClient {

switch reason {
case .categoryChange, .override, .routeConfigurationChange:
// Update speaker state based on current output
// Update internal speaker state based on current output
let isSpeaker = output.portType == .builtInSpeaker
_isSpeakerEnabled = isSpeaker

// Notify UI of the change
// Notify observers about the route change
NotificationCenter.default.post(
name: NSNotification.Name("AudioRouteChanged"),
object: nil,
Expand Down
74 changes: 52 additions & 22 deletions TelnyxRTC/Telnyx/TxClientDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,77 @@

import Foundation

/// Delegate protocol asociated with the TxClient
/// Methods for receiving TxClient events.
/// The TxClientDelegate protocol defines methods for receiving events and updates from a TxClient instance.
/// Implement this protocol to handle various states and events in your WebRTC-enabled application,
/// including connection status, call state changes, and push notifications.
///
/// ## Usage Example:
/// ```swift
/// class CallHandler: TxClientDelegate {
/// func onSocketConnected() {
/// print("Connected to Telnyx backend")
/// }
///
/// func onIncomingCall(call: Call) {
/// // Handle incoming call
/// call.answer()
/// }
///
/// // Implement other required methods...
/// }
/// ```
public protocol TxClientDelegate: AnyObject {

/// Tells the delegate when the Telnyx Client has successfully connected to the Telnyx Backend
/// Called when the WebSocket connection to Telnyx's backend is established.
/// This indicates a successful network connection, but the client may not be fully ready yet.
/// Wait for `onClientReady` before initiating calls.
func onSocketConnected()

/// Tells the delegate when the Telnyx Client has disconnected from the Telnyx Backend
/// Called when the WebSocket connection to Telnyx's backend is lost or closed.
/// The client will automatically attempt to reconnect unless explicitly disconnected.
func onSocketDisconnected()

/// Tells the delegate when there's an error in the Telnyx Client
/// - Parameter error: error occurred inside the Telnyx Client
/// Called when an error occurs in the TxClient.
/// - Parameter error: The error that occurred. Check the error type and message for details.
/// Common errors include authentication failures and network connectivity issues.
func onClientError(error: Error)

/// Tells the delegate that the The Telnyx Client is ready to be used.
/// Has successfully connected and logged in
/// Called when the client has successfully connected AND authenticated.
/// The client is now ready to make and receive calls.
/// This is the appropriate time to enable UI elements for calling functionality.
func onClientReady()

/// Called when push notification status changes for the current user.
/// - Parameters:
/// - success: Whether the push notification operation succeeded
/// - message: Descriptive message about the operation result
func onPushDisabled(success: Bool, message: String)

/// Push notification is disabled for the current user
func onPushDisabled(success:Bool,message:String)

/// Tells the delegate that the Telnyx Client session has been updated.
/// - Parameter sessionId: The new sessionId assigned to the client connection.
/// Called when the client's session is updated, typically after a reconnection.
/// - Parameter sessionId: The new session identifier for the connection.
/// Store this ID if you need to track or debug connection issues.
func onSessionUpdated(sessionId: String)

/// Tells the delegate that a call has been updated.
/// Called whenever a call's state changes (e.g., ringing, answered, ended).
/// - Parameters:
/// - callState: The new call state
/// - callId: The UUID of the affected call
/// - callState: The new state of the call (NEW, CONNECTING, RINGING, ACTIVE, HELD, DONE)
/// - callId: The unique identifier of the affected call
/// Use this to update your UI to reflect the current call state.
func onCallStateUpdated(callState: CallState, callId: UUID)

/// Tells the delegate that someone is calling
/// - Parameter call: The call object of the incoming call.
/// Called when a new incoming call is received.
/// - Parameter call: The Call object representing the incoming call.
/// You can use this object to answer or reject the call.
func onIncomingCall(call: Call)

/// Tells the delegate that a call has ended
/// - Parameter callId: the UUID of the call that has ended.
/// Called when a remote party ends the call.
/// - Parameter callId: The unique identifier of the ended call.
/// Use this to clean up any call-related UI elements or state.
func onRemoteCallEnded(callId: UUID)

/// Tells the delegate that an INVITE has been received for the incoming push
/// - Parameter call: The call object of the incoming call.
/// Called when a push notification triggers an incoming call.
/// - Parameter call: The Call object created from the push notification data.
/// This is specifically for handling calls that arrive via push notifications
/// when the app is in the background.
func onPushCall(call: Call)
}
Loading
Loading