# Audio calling 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](/docs/in-app-calling/ios/calling#request-user-permission-for-using-the-microphone). ## App-to-app calling Use the call client to start the call using `callUser(withId:)`, passing the user identifier of the callee (the destination user) as an argument. ```swift guard let callClient = sinchClient?.callClient else { return } let callResult = callClient.callUser(withId: "") 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](/docs/in-app-calling/ios/calling#handling-incoming-calls). Important For *App-to-App* calls, you must enable managed push by calling `sinchClient.enableManagedPushNotifications()`, even if you are the caller. See the full setup in [Push notifications](/docs/in-app-calling/ios/push-notifications). ## App-to-PSTN calling 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](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537) and should be prefixed with a ‘+’. ```swift 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. ### Presenting a number to the destination you are calling Mandatory step! 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](https://community.sinch.com/t5/Virtual-Numbers/How-can-I-activate-my-free-number-for-testing/ta-p/8578) that is available for free. If you have already upgraded your account, you can purchase a number to use on the [dashboard](https://dashboard.sinch.com/numbers/your-numbers). Note: When your account is in trial mode, you can only call your [verified numbers](https://dashboard.sinch.com/numbers/verified-numbers). If you want to call any number, you need to upgrade your account! Credits Required Remember, placing an App-to-PSTN call requires that your Sinch account has sufficient credits. Add credits on your [Account](https://dashboard.sinch.com/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. ```swift do { self.sinchClient = try SinchRTC.client(withApplicationKey: "", environmentHost: "ocra.api.sinch.com", userId: "", cli: "") } catch { // Handle error } ``` Testing DTMF 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. ## App-to-SIP calling An App-to-SIP call is a call that's made to a [SIP](https://en.wikipedia.org/wiki/Session_Initiation_Protocol) server. Use `callSIP(_:)` or `callSIP(_:headers:)` to initiate a SIP call. The destination SIP identity follows the form of email addresses ([user@domain](mailto:user@domain)), for example [alice@sip.your-sip-provider.com](mailto:alice@sip.your-sip-provider.com). ```swift guard let callClient = sinchClient?.callClient else { return } let callResult = callClient.callSIP("") 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-`. ## App-to-conference calling An App-to-Conference call can connect a user to a conference room where multiple users can be connected at the same time. ```swift guard let callClient = sinchClient?.callClient else { return } let callResult = callClient.callConference(withId: "") 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](/docs/voice/api-reference/voice/callouts). Note: The identifier for a conference room may not be longer than 64 characters. ## Handling incoming calls 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](https://developer.apple.com/documentation/callkit) (or [LiveCommunicationKit](https://developer.apple.com/documentation/livecommunicationkit) on iOS 17.4+), use `client(_:didReceiveIncomingCall:)` primarily to associate the `SinchCall` with the system call. For LiveCommunicationKit specifics, see [Push Notifications — LiveCommunicationKit](/docs/in-app-calling/ios/push-notifications#livecommunicationkit). Keep a mapping between system UUIDs and `callId`. ```swift extension SinchClientMediator: SinchCallClientDelegate { func client(_ client: SinchRTC.SinchCallClient, didReceiveIncomingCall call: SinchRTC.SinchCall) { call.delegate = self // Store/match call.callId with your CallKit UUID mapping } } ``` See our CallKit / LiveCommunicationKit sample apps 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](https://github.com/sinch/rtc-reference-applications/tree/master/ios). If you are **not** using *VoIP Push Notifications* and [CallKit](https://developer.apple.com/documentation/callkit) (or [LiveCommunicationKit](https://developer.apple.com/documentation/livecommunicationkit)), then use `client(_:didReceiveIncomingCall:)` to assign a delegate, and present a call UI. ```swift extension SinchClientMediator: SinchCallClientDelegate { func client(_ client: SinchRTC.SinchCallClient, didReceiveIncomingCall call: SinchRTC.SinchCall) { call.delegate = self // Present UI for call } } ``` ### Receiving Calls from PSTN or SIP (Phone-to-App / SIP-to-App) The Sinch SDK supports receiving incoming calls that originate from the PSTN (regular phone network) or from SIP endpoints. When a call arrives at a Sinch voice number or via SIP origination, the Sinch platform triggers an **Incoming Call Event (ICE)** callback to your backend. Out platform can then route this call to an in-app user by responding with the `connectMxp` SVAML action. #### Prerequisites 1. Rent a voice number from the [Sinch Build Dashboard](https://dashboard.sinch.com/numbers/overview) and assign it to your application, OR configure SIP origination for your application 2. Configure a callback URL in your app's Voice settings where Sinch will send call-related events 3. Implement the ICE callback handler in your backend to route calls to the appropriate app user #### Backend Implementation When a PSTN or SIP call comes in, respond to the ICE callback with a `connectMxp` action to route the call to an app user: ```json { "action": { "name": "connectMxp", "destination": { "type": "username", "endpoint": "target-user-id" } } } ``` ### Incoming video 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](/docs/in-app-calling/ios/video-calling) for details on how to add video views. ### Answering an incoming call To answer a call, use `call.answer()` when user presses the button. ```swift 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. ### Declining an incoming call 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: ```swift call.hangup() ``` ## Disconnecting a call 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: ```swift 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: ```swift // SinchCallDelegate func callDidEnd(_ call: SinchCall) { // Update UI, e.g., dismiss the call screen } ``` ## Request user permission for using the microphone 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](https://developer.apple.com/documentation/bundleresources/information_property_list/nsmicrophoneusagedescription). See Apple documentation on [`AVCaptureDevice.requestAccess(for:completionHandler:)`](https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624584-requestaccessformediatype) for details on how to request user permission. Note: 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.