# Getting started with the Sinch In‑app Calling iOS SDK

This guide shows you how to get started integrating your iOS application with the In‑app Calling iOS SDK. Because we're just getting started, this guide covers how to create a Sinch client and how to make and receive calls with either `CallKit` or `LiveCommunicationKit`.

## Prerequisites

- Xcode available [here](https://developer.apple.com/xcode/).
- In‑app Calling [SDK for iOS](/docs/in-app-calling/sdk-downloads/).
- APNs signing key from your [Apple Developer Account](https://developer.apple.com/) uploaded to your [Sinch Developer Account](https://dashboard.sinch.com/voice/apps).


Note
In this guide we use the Swift SDK (`SinchRTC.xcframework`). From **v.5.39** only this SDK is available. The Objective-C SDK (`Sinch.xcframework`) is no longer distributed.

Important for Objective-C Projects
If you have a pure Objective-C project, you **might need to** enable Swift runtime support in your project. Add an empty Swift file (e.g., `SwiftRuntimeLoader.swift`) to your project and create a bridging header when Xcode prompts you. This is required because the Sinch SDK contains Swift code. For detailed instructions, see the [Swift Runtime Support section](/docs/in-app-calling/ios/miscellaneous/#swift-runtime-support-for-objective-c-projects).

## Reference applications and additional files

- Swift reference application on [GitHub](https://github.com/sinch/rtc-reference-applications/tree/master/ios).
- Sample applications are bundled with the SDK package.
- Additional [iOS documentation](/docs/in-app-calling/ios/first-time-setup/)


## Upload your APNs signing keys

When a call is placed from UserA to UserB, the Sinch backend delivers a VoIP push notification via APNs to UserB in order to initiate the call.
To allow Sinch to send push notifications on your behalf, you must upload your APNs signing key to your Sinch application configuration. You can find instructions
on how to generate and upload your APNs signing key [here](/docs/in-app-calling/ios/push-notifications/#configuring-an-apns-authentication-signing-key).

## Setup the Xcode project

1. Start by creating a new iOS app using Xcode. Select Swift as the language for your app.
2. Download the Sinch Swift SDK from the [SDK download page](/docs/in-app-calling/sdk-downloads/) and decompress it. Add the `SinchRTC.xcframework` folder to your project and make sure it is set to Embed & Sign, otherwise you'll experience `dylib` loading failures.
3. You can use the `ringback.wav` and `ringtone.wav` audio files from the decompressed SDK folder (in the sample application), from the reference applications on GitHub, or provide your own audio files and add them to your app bundle.
4. In your project's `Target`, go to the `Signing & Capabilities` tab and enable "Voice over IP" under `Background Modes` to integrate `CallKit` / `LiveCommunicationKit`. Calls through `CallKit` / `LiveCommunicationKit` will fail if you skip this step.
5. While in your project's `Target` → `Signing & Capabilities`, add the "Push Notifications" capability. Without this capability, the app can't acquire a device token or receive notifications.


## Create the app views

Using the Xcode storyboard, create three view controllers named as follows:

- `LoginViewController.swift` — view controller for creating a client and logging in a user
- `MainViewController.swift` — view controller from which calls will be initiated and received
- `AudioCallViewController.swift` — view controller with calling actions and information


## Enable Sinch logs

When debugging, it can be useful to set a log callback to enable Sinch logging and make it easier to understand potential problems. You can do that by invoking `SinchRTC.setLogCallback()`. The example below extends Sinch `LogSeverity` to map Sinch log severity to `OSLogType`:

*AppDelegate.swift*


```swift
import OSLog
import SinchRTC

class AppDelegate: UIResponder, UIApplicationDelegate {

  func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions:
                           [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Sinch Logging setup.
    SinchRTC.setLogCallback { (severity: SinchRTC.LogSeverity, 
                                   area: String, 
                                    msg: String, 
                                      _: Date) in
      os_log("%{public}@", log: .sinchOSLog(for: area), type: severity.osLogType, msg)
    }

    return true
  }
}

// Map Sinch log severity to OSLogType.
extension LogSeverity {

  var osLogType: OSLogType {
    switch self {
      case .info, .warning: return .default
      case .critical: return .fault
      case .trace: return .debug
      default: return .default
    }
  }
}
```

Note:
Debug logs can be verbose and may contain misleading messages. Enable logging only when investigating specific errors or behavior related to Sinch components.

## User registration

In order to place calls between two users, you must register the users on the Sinch backend.

### SinchClientMediator

First, create a `Config.swift` file to store the credentials of your Sinch app from your [dashboard](https://dashboard.sinch.com/voice/apps). These are used to authenticate your Sinch client to the Sinch backend.

*Config.swift*


```swift
let APPLICATION_KEY = "..."

// Developers must not, under any circumstances, commit APPLICATION_SECRET in client code.
// This introduces a serious security risk.
let APPLICATION_SECRET = "..."

let ENVIRONMENT_HOST = "ocra.api.sinch.com"
```

Create a `SinchClientMediator` class. This class will act as:

- a wrapper around the root Sinch component, `SinchClient`
- the delegate of `SinchClient`


In particular, `SinchClientMediator` owns a `SinchClient` object, and implements `createAndStartClient(with userId: String, and callback: @escaping (_ error: Error?) -> Void)`, which creates and starts its Sinch client.

Note:
The completion callback is stored in a property, so that it can be accessed later in the `SinchClient` delegate callback.

In this example app, the instance of `SinchClientMediator` is owned by `AppDelegate`, and initialized in `AppDelegate.application(didFinishLaunchingWithOptions:)`.
Make sure the `SinchClientMediator` variable is public, so that ViewControllers can access it later.

*SinchClientMediator.swift*


```swift

typealias ClientStartedCallback = (_ error: Error?) -> Void

class SinchClientMediator {

  var clientStartedCallback: ClientStartedCallback!
  
  private(set) var sinchClient: SinchClient?
  
  // Creating and starting a client for particular user.
  // https://developers.sinch.com/docs/in-app-calling/ios/sinch-client/#creating-the-sinclient
  func createAndStartClient(with userId: String, and callback: @escaping (_ error: Error?) -> Void) {
    do {
      sinchClient = try SinchRTC.client(withApplicationKey: APPLICATION_KEY,
                                        environmentHost: ENVIRONMENT_HOST,
                                        userId: userId)
    } catch let error as NSError {
      os_log("Failed to create sinchClient",
             log: .sinchOSLog(for: SinchClientMediator.identifier),
             type: .info,
             error.localizedDescription)

      callback(error)
    }

    clientStartedCallback = callback

    guard let sinchClient = sinchClient else { return }

    sinchClient.delegate = self
    sinchClient.start()
  }
}
```

Now, create an extension of `SinchClientMediator` to conform to the `SinchClientDelegate` protocol, implementing credential provisioning and monitoring of the Sinch client status.

Note:
The stored copy of `clientStartedCallback` is used and reset in `clientDidStart()` and `clientDidFail()`.

*SinchClientMediator+SinchClientDelegate.swift*


```swift
// Extension to implement credential provisioning and monitoring of Sinch client status.
extension SinchClientMediator: SinchClientDelegate {

  // Client authorizing with JWT.
  // https://developers.sinch.com/docs/in-app-calling/ios/sinch-client/#authorizing-the-client
  func clientRequiresRegistrationCredentials(_ client: SinchRTC.SinchClient,
                                             withCallback callback: SinchRTC.SinchClientRegistration) {
    do {
      // WARNING: test implementation to create JWT token, shouldn't be done for production application.
      let jwt = try SinchJWT.sinchJWTForUserRegistration(withApplicationKey: APPLICATION_KEY,
                                                         applicationSecret: APPLICATION_SECRET,
                                                         userId: client.userId)

      callback.register(withJWT: jwt)
    } catch {
      callback.registerDidFail(error: error)
    }
  }

  func clientDidStart(_ client: SinchClient) {
    guard clientStartedCallback != nil else { return }

    clientStartedCallback(nil)
    clientStartedCallback = nil
  }

  func clientDidFail(_ client: SinchClient, error: Error) {
    guard clientStartedCallback != nil else { return }

    clientStartedCallback(error)
    clientStartedCallback = nil
  }
}
```

Note:
To simplify this tutorial, the JWT token required to authenticate the Sinch client (see `SinchClient` [docs](/docs/in-app-calling/ios/sinch-client/)) is generated in the client app.
This is bad security practice, as it stores the application secret in client code. In your application, JWT generation must be delegated to your backend. For an example implementation of JWT user registration, refer to the `SinchJWT.swift` file in Sinch's Swift reference application or in the sample application bundled with the Swift SDK.

## User login

Add a property of type `SinchClientMediator` to `LoginViewController`, and assign it from the `SinchClientMediator` member of `AppDelegate`.

*LoginViewController.swift*


```swift
final class LoginViewController: UIViewController {

  // From AppDelegate pass clientMediator to LoginViewController.
  var clientMediator: SinchClientMediator?
}
```

Then, using the Connections Inspector, implement `SinchClient` instantiation and user registration in response to tapping the "Login"
button in `LoginViewController`.
If the Sinch client is created and started successfully, transition to `MainViewController`.

*LoginViewController.swift*


```swift
final class LoginViewController: UIViewController {

  ...

  @IBAction func login(_ sender: UIButton) {
    // Assume `username` is read from a text field.
    clientMediator?.createAndStartClient(with: username) { [weak self] error in
      guard let self = self else { return }
      
      if let error = error {
        os_log("SinchClient started with error: %{public}@",
          log: .sinchOSLog(for: LoginViewController.identifier),
          type: .error,
          error.localizedDescription)
      }
      
      presentMainViewController()
    }
  }
  
  private func presentMainViewController() {
    // prepareViewController(identifier: "main") is a UIViewController extension.
    // that instantiates a view controller from the storyboard.
    let mainViewController: MainViewController? = prepareViewController(identifier: "main")

    guard let mainViewController = mainViewController else {
      preconditionFailure("Error MainViewController is expected")
    }

    mainViewController.clientMediator = clientMediator
    
    os_log("Preparing MainViewController for presentation", log: .sinchOSLog(for: LoginViewController.identifier))

    present(mainViewController, animated: true)
  }
}
```

## Next steps

Now that your application is created, you can configure that application to make and receive a call with `CallKit` / `LiveCommunicationKit`.

Make and receive a call with CallKit
Make and receive a call with LiveCommunicationKit