# 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're using the Swift SDK, but the Objective-C SDK is also available. ## 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