Set up voice calling with the Sinch Voice & Video SDK. The SDK supports four types of calls: App-to-App (audio or video), App-to-PSTN, App-to-SIP and App-to-Conference calls.
The Sinch SDK supports four types of calls: App-to-App (audio or video), App-to-PSTN, App-to-SIP and App-to-Conference calls. The SinchCallClient is the entry point for the calling functionality of the Sinch SDK.
Calls are placed using SinchCallClient and events are received via SinchCallClientDelegate. The call client is owned by the Sinch client (SinchClient) and accessed using sinchClient.callClient.
Before you start, ensure your application is requesting user permission for using the microphone.
Use the call client to start the call using callUser(withId:), passing the user identifier of the callee (the destination user) as an argument.
guard let callClient = sinchClient?.callClient else { return }
let callResult = callClient.callUser(withId: "<remote user id>")
switch callResult {
case .success(let call):
call.delegate = self
case .failure(let error):
// Handle error
}A call object is returned, containing details about the participants in the call, call details such as start time, call state, and possible errors.
Assuming the callee's device is available and responsive, the delegate method callDidProgress(_:) will be called. It notifies the application that the outbound call is progressing. If a progress tone should be played, this is where it should be started.
After the other party answers delegate method callDidAnswer(_:) will be called and callDidEstablish(_:) will be called when actual call was established. Now, the users can start talking.
Also see Handling Incoming Calls.
An App-to-PSTN call is a call that's made to a phone on the regular telephone network. Setting up an App-to-PSTN call is not much different from setting up an App-to-App call.
Initiate a call to a phone number by calling callPhoneNumber(_:). The phone number should be specified according to the E.164 number formatting standard and should be prefixed with a ‘+’.
guard let callClient = sinchClient?.callClient else { return }
let callResult = callClient.callPhoneNumber("+14155550101")
switch callResult {
case .success(let call):
call.delegate = self
case .failure(let error):
// Handle error
}Example: To call the US phone number 415 555 0101, the phone number should be specified as +14155550101. The ‘+’ is the required prefix and the US country code ‘1’ prepended to the local subscriber number.
You must provide a CLI (Calling Line Identifier) or your call will fail.
You need a number from Sinch so you can provide a valid CLI to the handset you are calling. If you have a trial account, you can use the test number that is available for free. If you have already upgraded your account, you can purchase a number to use on the dashboard.
When your account is in trial mode, you can only call your verified numbers. If you want to call any number, you need to upgrade your account!
Remember, placing an App-to-PSTN call requires that your Sinch account has sufficient credits. Add credits on your Account page. Credits are used each time an App-to-PSTN call is placed and your account balance is updated after each call.
When instantiating the SinchClient, provide the cli (Calling Line Identifier) with a number from your account.
do {
self.sinchClient = try SinchRTC.client(withApplicationKey: "<application key>",
environmentHost: "ocra.api.sinch.com",
userId: "<user id>",
cli: "<Your Purchased Sinch Number>")
} catch {
// Handle error
}In an App-to-PSTN scenario, it's possible to issue DTMF sequences using the Sinch SDK. Note that if the receiving end of the call is an iOS device, you might have to disable VoLTE ("Voice over LTE") option in the settings of the phone at the receiving end of the call in order to be able to hear DTMF tones.
An App-to-SIP call is a call that's made to a SIP server. Use callSIP(_:) or callSIP(_:headers:) to initiate a SIP call. The destination SIP identity follows the form of email addresses (user@domain), for example alice@sip.your-sip-provider.com.
guard let callClient = sinchClient?.callClient else { return }
let callResult = callClient.callSIP("<SIP identity>")
switch callResult {
case .success(let call):
call.delegate = self
case .failure(let error):
// Handle error
}When custom SIP headers are passed using callSIP(_:headers:), the headers should be prefixed with x-.
An App-to-Conference call can connect a user to a conference room where multiple users can be connected at the same time.
guard let callClient = sinchClient?.callClient else { return }
let callResult = callClient.callConference(withId: "<conference id>")
switch callResult {
case .success(let call):
call.delegate = self
case .failure(let error):
// Handle error
}It's also possible to connect users to a conference call via the Sinch REST API.
The identifier for a conference room may not be longer than 64 characters.
To act on incoming calls, implement SinchCallClientDelegate and assign a delegate to the call client. The call client delegate is notified of new incoming calls via client(_:didReceiveIncomingCall:).
To get events related to the call, assign a call delegate. The call object contains details about participants, start time, potential error codes, and error messages.
If you're using VoIP Push Notifications and CallKit (or LiveCommunicationKit on iOS 17.4+), use client(_:didReceiveIncomingCall:) primarily to associate the SinchCall with the system call. For LiveCommunicationKit specifics, see Push Notifications — LiveCommunicationKit. Keep a mapping between system UUIDs and callId.
extension SinchClientMediator: SinchCallClientDelegate {
func client(_ client: SinchRTC.SinchCallClient,
didReceiveIncomingCall call: SinchRTC.SinchCall) {
call.delegate = self
// Store/match call.callId with your CallKit UUID mapping
}
}In the Sinch SDK download, there is a sample app SinchReferenceApp.xcodeproj with a detailed example on how to associate SinchCall with CallKit calls. Or check our Swift reference application on GitHub.
If you are not using VoIP Push Notifications and CallKit (or LiveCommunicationKit), then use client(_:didReceiveIncomingCall:) to assign a delegate, and present a call UI.
extension SinchClientMediator: SinchCallClientDelegate {
func client(_ client: SinchRTC.SinchCallClient,
didReceiveIncomingCall call: SinchRTC.SinchCall) {
call.delegate = self
// Present UI for call
}
}A video call will, just like a voice-only call, trigger client(_:didReceiveIncomingCall:). To determine whether an incoming call contains a video stream, use call.details.isVideoOffered. Also see Video Calling for details on how to add video views.
To answer a call, use call.answer() when user presses the button.
call.answer()Now, the clients on both ends will establish the connection. When the call is established and the voice streams are running in both directions, the callDidEstablish(_:) delegate method is called.
If the call shouldn't be answered, use call.hangup() to decline the call. The caller will be notified that the incoming call was denied (via callDidEnd(_:)).
User presses the hangup button:
call.hangup()When the user wants to disconnect an ongoing call, use call.hangup(). Either user taking part in a call can disconnect it.
Hanging up a call:
call.hangup()When either party disconnects a call, the application is notified using callDidEnd(_:). This allows the user interface to be updated, an alert tone to be played, or similar actions to occur.
Handling a call that ends:
// SinchCallDelegate
func callDidEnd(_ call: SinchCall) {
// Update UI, e.g., dismiss the call screen
}Recording audio always requires explicit permission from the user. Your app must provide a description for its use of the microphone in terms of the Info.plist key NSMicrophoneUsageDescription.
See Apple documentation on AVCaptureDevice.requestAccess(for:completionHandler:) for details on how to request user permission.
Unless the application has explicitly requested permission to use the microphone, the user is shown a dialog the first time the microphone is activated. This may not provide the best user experience, so we advise explicitly requesting microphone permission in advance.