# Quickstart (/en/realtime-media/voice/quickstart/ios)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

This iOS quickstart shows you how to create a basic Voice Calling app using the Agora Voice SDK.

    ## Understand the tech [#understand-the-tech-1]

    To start a Voice Calling session, implement the following steps in your app:

    * **Initialize the Agora Engine**: Before calling other APIs, create and initialize an Agora Engine instance.

    * **Join a channel**: Call methods to create and join a channel.

    * **Send and receive audio**: All users can publish streams to the channel and subscribe to audio streams published by other users in the channel.

    ![Video calling workflow](https://assets-docs.agora.io/images/voice-sdk/get-started-sdk-voice.svg)

    ## Prerequisites [#prerequisites-1]

    * Xcode 13.0 or higher.

    * An Apple developer account.

    * If you need to use CocoaPods integrated SDK, make sure [CocoaPods is installed](https://guides.cocoapods.org/using/getting-started.html#getting-started).

    * Two devices running iOS 14.0 or higher.

    * A microphone

    * A valid Agora account and project. Please refer to [Agora account management](manage-agora-account.md) for details.

    ## Set up your project [#set-up-your-project-1]

    This section shows you how to set up your iOS project and install the Agora Voice SDK.

    <Tabs>
      <TabsList>
        <TabsTrigger value="new-project">
          Create a new project
        </TabsTrigger>

        <TabsTrigger value="existing-project">
          Add to an existing project
        </TabsTrigger>
      </TabsList>

      <TabsContent value="new-project">
        Follow these steps to create a project in Xcode:

        1. Refer to [Create a project](https://help.apple.com/xcode/mac/current/#/dev07db0e578). Under **Application** select **App**. Use **Storyboard** for the user interface and choose **Swift** as the programming language.

           <CalloutContainer type="info">
             <CalloutDescription>
               If you have not added the development team information, you see the &#x2A;*Add account...** button. Click the button and follow the on-screen prompts to log in to your Apple ID. Once login is complete, click **Next**, and choose your Apple account as the development team.
             </CalloutDescription>
           </CalloutContainer>

        2. [Set up automatic signing](https://help.apple.com/xcode/mac/current/#/dev23aab79b4) for your projects.

        3. [Set the target devices](https://help.apple.com/xcode/mac/current/#/deve69552ee5) where your app will be deployed.

        4. Create a user interface for your app. Refer to [Create a user interface](#create-a-user-interface) to create a bare bones UI.
      </TabsContent>

      <TabsContent value="existing-project">
        Follow these steps to add Voice Calling to your Xcode project:

        1. Open your project in Xcode.

        2. [Set the target devices](https://help.apple.com/xcode/mac/current/#/deve69552ee5) where your app will be deployed.

        3. Create a user interface for your app. Refer to [Create a user interface](#create-a-user-interface) to create a bare bones UI.
      </TabsContent>
    </Tabs>

    ### Install the SDK [#install-the-sdk-1]

    Use one of the following methods to install Voice SDK.

    <Tabs>
      <TabsList>
        <TabsTrigger value="swift-package-manager">
          Swift Package Manager
        </TabsTrigger>

        <TabsTrigger value="cocoapods">
          CocoaPods
        </TabsTrigger>

        <TabsTrigger value="manual-integration">
          Manual integration
        </TabsTrigger>
      </TabsList>

      <TabsContent value="swift-package-manager">
        1. In Xcode, go to **File** > **Add Package Dependencies**.

        2. In the search bar, past the following URL:

           ```json
           {`https://github.com/AgoraIO/AgoraRtcEngine_iOS.git`}
           ```

        3. Click **Add Package**, select the latest version, and click **Next**.

        4. For basic Voice Calling, select **RtcBasic**.

           If needed, also select:

           * **`SpatialAudio`** for spatial audio effects.
           * **`VirtualBackground`** for virtual background.

        5. Under **Add to Target**, select your project and click **Add Package**.

           For further information, refer to [Apple's official documentation](https://help.apple.com/xcode/mac/current/#/devb83d64851).
      </TabsContent>

      <TabsContent value="cocoapods">
        1. Go to the project root directory in the terminal and run the `pod init` command. A text file named `Podfile` is generated in the project folder.

        2. Open `Podfile` and modify the content as follows. Replace `Your App` with your target name.

           ```ruby
           platform :ios, '9.0'
             target 'Your App' do
              # For x.y.z fill in the specific SDK version number, such as 4.4.0.
              pod 'AgoraAudio_iOS', 'x.y.z'
             end
           ```

           Obtain the latest version number from the [release notes](reference/release-notes.mdx).

        3. Run the `pod install` command in the terminal to install the Voice SDK. After successful installation, the terminal shows &#x2A;*Pod installation complete!**.

        4. After successful installation, a file with the suffix `.xcworkspace` is generated in the project folder. Open the file through Xcode for subsequent operations.
      </TabsContent>

      <TabsContent value="manual-integration">
        1. Download the latest version of the SDK from [SDKs download](/en/api-reference/sdks?product=voice\&platform=ios) and extract the contents.

        2. Copy the files in the `libs` folder of the SDK package to your project directory.

        3. Open Xcode and [add the corresponding dynamic library](https://help.apple.com/xcode/mac/current/#/dev51a648b07). Make sure the **Embed** property of the added dynamic library is set to **Embed & Sign**.

           <CalloutContainer type="info">
             <CalloutDescription>
               Agora SDK uses `libc++` (LLVM) by default. If you need to use `libstdc++` (GNU), please contact [support@agora.io](mailto\:support@agora.io). The library provided by the SDK is a FAT Image, which includes 32/64-bit simulator and 32/64-bit real machine versions.
             </CalloutDescription>
           </CalloutContainer>
      </TabsContent>
    </Tabs>

    <CalloutContainer type="info">
      <CalloutDescription>
        The [privacy updates for App Store submissions](https://developer.apple.com/news/?id=r1henawx) released by Apple, require developers to declare approved reasons for using a set of APIs in their app’s privacy manifest. Agora provides a [`PrivacyInfo.xcprivacy`](https://download.agora.io/sdk/release/PrivacyInfo.xcprivacy) file that you can include in your project.
      </CalloutDescription>
    </CalloutContainer>

    ## Implement Voice Calling [#implement-voice-calling-1]

    This section guides you through the implementation of basic real-time audio interaction in your app.

    The following figure illustrates the essential steps:

    ![image](https://assets-docs.agora.io/images/video-sdk/quickstart-voice-call-sequence.svg)

    This guide includes [complete sample code](#complete-sample-code) that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.

    ### Import Agora framework [#import-agora-framework]

    Add the following import to your swift file:

    ```swift
    import AgoraRtcKit
    ```

    ### Initialize the engine [#initialize-the-engine-1]

    Call `sharedEngine(withAppId:delegate:)` to create and initialize an `AgoraRtcEngineKit` instance. Provide your [App ID](manage-agora-account.md) and an `AgoraRtcEngineDelegate` implementation to [handle SDK events](#subscribe-to-voice-sdk-events).

    ```swift
    var agoraKit: AgoraRtcEngineKit!
    let appId = "YOUR_AGORA_APP_ID"

    // Initialize the Agora engine
    func initializeAgoraVoiceSDK() {
      agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: appId, delegate: self)
    }
    ```

    ### Join a channel [#join-a-channel-1]

    To join a channel, call `joinChannel` with the following parameters:

    * **Channel name**: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.

    * **Authentication token**: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a [token server](build/set-up-token-authentication/deploy-token-server.mdx) in your security infrastructure. For the purpose of this guide [Generate a temporary token](manage-agora-account.md).

    * **User ID**: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to `0` when joining a channel, the SDK generates a random number for the user ID and returns the value in the `didJoinChannel` callback.

    * **Channel media options**: Configure `AgoraRtcChannelMediaOptions` to define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.

    For Voice Calling, set the `channelProfile` to `.communication` and the `clientRoleType` to `.broadcaster`.

    ```swift
    let channelName = "demo"  // Replace with your actual channel name
    let token = "<Authentication token>" // Replace with your token

    func joinChannel() {
      let options = AgoraRtcChannelMediaOptions()
      options.channelProfile = .communication
      // Set user role to broadcaster; to set user role to audience, keep the default value
      options.clientRoleType = .broadcaster
      // Publish microphone audio
      options.publishMicrophoneTrack = true
      // Automatically subscribe to all audio streams
      options.autoSubscribeAudio = true
      // Join the channel using a temporary token and channel name
      agoraKit.joinChannel(
        byToken: token,
        channelId: channelName,
        uid: 0,
        mediaOptions: options
      )
    }
    ```

    ### Subscribe to Voice SDK events [#subscribe-to-voice-sdk-events-1]

    The Voice SDK provides a delegate for handling channel events. To use it, conform to the `AgoraRtcEngineDelegate` protocol in your class and implement the event methods you want to handle. The following code implements the `didJoinChannel`, `didOfflineOfUid`, and `didJoinedOfUid` callbacks:

    <CalloutContainer type="info">
      <CalloutDescription>
        To ensure that you receive all Voice SDK events, set the Agora Engine event handler before joining a channel.
      </CalloutDescription>
    </CalloutContainer>

    ```swift
    // Extension for handling Agora SDK callbacks
    extension ViewController: AgoraRtcEngineDelegate {

      // Triggered when the local user successfully joins a channel
      func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
        print("Successfully joined channel: \(channel) with UID: \(uid)")
      }

      // Triggered when a remote user joins the channel
      func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
        print("User \(uid) joined after \(elapsed) milliseconds")
      }
      // Triggered when a remote user leaves the channel
      func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
        print("User \(uid) left: Reason -> \(reason)")
      }
    }
    ```

    To learn about the other SDK events, see [`AgoraRtcEngineDelegate`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorartcenginedelegate).

    ### Display the local video [#display-the-local-video]

    Call `setupLocalVideo` to initialize the local view and set the local video display properties.

    ```swift
    // Configures and starts displaying the local video feed
    func setupLocalVideo() {
      let videoCanvas = AgoraRtcVideoCanvas()
      videoCanvas.view = localView
      videoCanvas.uid = 0 // UID 0 is assigned to the local user
      videoCanvas.renderMode = .hidden
      agoraKit.setupLocalVideo(videoCanvas)
    }
    ```

    ### Display remote video [#display-remote-video]

    To initialize the remote user view, call `setupRemoteVideo` and set the local display properties for the remote user. Use the `didJoinedOfUid` callback to get the UID of the remote user.

    ```swift
    func setupRemoteVideo(uid: UInt, view: UIView?) {
      let videoCanvas = AgoraRtcVideoCanvas()
      videoCanvas.uid = uid
      videoCanvas.view = view // Assign view for joining, set to nil for leaving
      videoCanvas.renderMode = .hidden
      agoraKit.setupRemoteVideo(videoCanvas)
    }
    ```

    ### Handle permissions [#handle-permissions-1]

    To access the camera and microphone on iOS devices, add the required permissions for real-time interaction. Open the `info.plist` file from the project navigation bar, [edit the property list](https://help.apple.com/xcode/mac/current/#/dev3f399a2a6), to add the required permissions. These permissions are optional. However, if you do not add these permissions, you will not be able to use the corresponding devices.

    | Key                                    | Type   | Value                                                                                                   |
    | :------------------------------------- | :----- | :------------------------------------------------------------------------------------------------------ |
    | Privacy - Microphone Usage Description | String | For the purpose of using the microphone. For example, for a call or live interactive streaming session. |

    <CalloutContainer type="info">
      <CalloutDescription>
        * If your project depends on third-party plugins or libraries, such as a third-party camera library, and the signature of the plug-in or library is inconsistent with the signature of the project, check the **Hardened Runtime** settings. Specifically, review and potentially disable **Runtime Exceptions** and **Library Validation** in the project configuration.
        * For further information, refer to [Preparing your app for distribution](https://developer.apple.com/documentation/xcode/preparing_your_app_for_distribution).
      </CalloutDescription>
    </CalloutContainer>

    ### Start and close the app [#start-and-close-the-app-1]

    When the user launches the app, it joins the channel and starts Voice Calling. When the user closes the app, it leaves the channel and ends Voice Calling.

    1. To start Voice Calling, call the following methods:

       ```swift
       // Initialize the Agora engine
       initializeAgoraVoiceSDK()
       // Join the channel
       joinChannel()
       ```

    2. To leave the channel and release SDK resources when the app is closed, call the following methods:

       ```swift
       // Leave the channel and release session-related resources
       agoraKit.leaveChannel(nil)
       // Release all resources used by the Agora SDK
       AgoraRtcEngineKit.destroy()
       ```

    <CalloutContainer type="warning">
      <CalloutDescription>
        After destroying the engine, you can no longer use SDK methods and callbacks. To use the real-time interaction functions again, create a new engine. See [Initialize the engine](#initialize-the-engine) for details.
      </CalloutDescription>
    </CalloutContainer>

    ### Complete sample code [#complete-sample-code-1]

    A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. Copy the following code into your `ViewController.swift` file:

    <Accordions>
      <Accordion title="Complete sample code for real-time Voice Calling">
        ```swift
        import UIKit
        import AgoraRtcKit

        class ViewController: UIViewController {

          let appId = "<Your app ID>" // Replace with your actual App ID
          let channelName = "demo"  // Replace with your actual channel name
          let token = "<Authentication token>" // Replace with your token

          var statusLabel: UILabel!
          // Instance of the Agora RTC engine
          var agoraKit: AgoraRtcEngineKit!

          override func viewDidLoad() {
            super.viewDidLoad()

            // Initialize the Agora engine
            initializeAgoraVoiceSDK()
            // Set up user interface
            setupUI();
            // Join an Agora channel
            joinChannel()
          }

          func setupUI() {
            let mainView = UIView(frame: view.bounds)
            mainView.backgroundColor = .black

            statusLabel = UILabel(frame: mainView.bounds)
            statusLabel.text = "Waiting for other user"
            statusLabel.textColor = .white
            statusLabel.textAlignment = .center
            statusLabel.autoresizingMask = [.flexibleWidth, .flexibleHeight]

            mainView.addSubview(statusLabel)
            view.addSubview(mainView)
          }

          // Clean up resources when the view controller is deallocated
          deinit {
            agoraKit.leaveChannel(nil)
            AgoraRtcEngineKit.destroy()
          }

          // Initializes the Voice SDK instance
          func initializeAgoraVoiceSDK() {
            // Create an instance of AgoraRtcEngineKit and set the delegate
            agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: appId, delegate: self)
          }

          // Join the channel with specified options
          func joinChannel() {
            let options = AgoraRtcChannelMediaOptions()
            // In voice calling, set the channel use-case to communication
            options.channelProfile = .communication
            // Set the user role as broadcaster (default is audience)
            options.clientRoleType = .broadcaster
            // Publish audio captured by microphone
            options.publishMicrophoneTrack = true
            // Auto subscribe to all audio streams
            options.autoSubscribeAudio = true
            // Use a temporary token to join the channel
            // If you set uid=0, the engine generates a uid internally; on success, it triggers didJoinChannel callback
            agoraKit.joinChannel(
              byToken: token,
              channelId: channelName,
              uid: 0,
              mediaOptions: options
            )
          }
        }

        // Extension for handling Agora SDK callbacks
        extension ViewController: AgoraRtcEngineDelegate {

          // Triggered when the local user successfully joins a channel
          func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
            print("Successfully joined channel: \(channel) with UID: \(uid)")
            statusLabel.text = "You Successfully joined. Waiting for other users"
          }

          // Triggered when a remote user joins the channel
          func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
            print("User \(uid) joined after \(elapsed) milliseconds")
            statusLabel.text = "User \(uid) joined"
          }

          // Triggered when a remote user leaves the channel
          func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
            print("User \(uid) left: Reason -> \(reason)")
            statusLabel.text = "User \(uid) left"
          }
        }
        ```
      </Accordion>
    </Accordions>

    ### Create a user interface [#create-a-user-interface-1]

    Based on your use-case, create a user interface for your project.

    The following example uses a `UILabel` to display the current app status. To use this interface, replace the contents of the `ViewController.swift` file with the following code:

    **Sample code to create the user interface**

    ```swift
    import UIKit

    class ViewController: UIViewController {
      var statusLabel: UILabel!

      override func viewDidLoad() {
        super.viewDidLoad()

        // Set up user interface
        setupUI();
      }

      func setupUI() {
        let mainView = UIView(frame: view.bounds)
        mainView.backgroundColor = .black

        statusLabel = UILabel(frame: mainView.bounds)
        statusLabel.text = "Waiting for other user"
        statusLabel.textColor = .white
        statusLabel.textAlignment = .center
        statusLabel.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        mainView.addSubview(statusLabel)
        view.addSubview(mainView)
      }
    }
    ```

    ## Test the sample code [#test-the-sample-code-1]

    Take the following steps to test the sample code:

    1. In your code update the `appId` and `token`, with the app ID and temporary token you obtained from Agora Console. Use the same `channelName` you filled in when generating the temporary token.

    2. Connect your iOS device to your computer.

    3. Click **Build** to run your project and wait a few seconds for the app installation to complete.

    4. Allow the app to access the device's microphone.

    5. If an untrusted developer prompt pops up on the device, click **Cancel** to close the prompt, then open **Settings > General > VPN and Device Management** on the iOS device, and choose to trust the developer in the **Developer APP**.

    6. On a second iOS device, repeat the previous steps to install and launch the app. Alternatively, use the [Web demo](https://webdemo.agora.io/basicVideoCall/index.html) to join the same channel and test the following use-cases:

       * If users on both devices join the channel as hosts, they can hear each other.
       * If one user joins as host and the other as audience, the audience can hear the host.

    ## Reference [#reference-1]

    This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

    * If a firewall is deployed in your network environment, refer to [Connect with Cloud Proxy](build/manage-connection-and-quality/cloud-proxy.mdx) to use Agora services normally.

    ### Next steps [#next-steps-1]

    After implementing the quickstart sample, read the following documents to learn more:

    * To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see [Secure authentication with tokens](build/set-up-token-authentication/use-tokens.mdx).

    ### Sample project [#sample-project-1]

    Agora provides open source sample projects on [GitHub](https://github.com/AgoraIO/API-Examples) for your reference. Download or view the [JoinChannelAudio](https://github.com/AgoraIO/API-Examples/tree/main/iOS/APIExample/APIExample/Examples/Basic/JoinChannelAudio) project for a more detailed example.

    ### Add a privacy manifest file [#add-a-privacy-manifest-file]

    The Agora Voice SDK for iOS provides the `PrivacyInfo.xcprivacy` file that contains the required reasons for the APIs used by the SDK. To add the privacy manifest to your app in Xcode, follow these steps:

    1. Create a privacy manifest in your app project:

       1. Choose **File > New File**.

       2. Scroll down to the **Resource** section and select **App Privacy File** type.

       3. Click **Next**.

       4. Check your app in the **Targets** list.

       5. Click **Create**.

       The default file name is `PrivacyInfo.xcprivacy` which is also the required file name for bundled privacy manifests.

    2. Add the items in `PrivacyInfo.xcprivacy` file of the Voice SDK to `PrivacyInfo.xcprivacy` of the app using the following source code:

       ```xml
       <?xml version="1.0" encoding="UTF-8"?>
       <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
       <plist version="1.0">
       <dict>
         <key>NSPrivacyTracking</key>
         <false/>
         <key>NSPrivacyCollectedDataTypes</key>
         <array/>
         <key>NSPrivacyAccessedAPITypes</key>
         <array>
           <dict>
             <key>NSPrivacyAccessedAPIType</key>
             <string>NSPrivacyAccessedAPICategorySystemBootTime</string>
             <key>NSPrivacyAccessedAPITypeReasons</key>
             <array>
               <string>35F9.1</string>
             </array>
           </dict>
           <dict>
             <key>NSPrivacyAccessedAPIType</key>
             <string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
             <key>NSPrivacyAccessedAPITypeReasons</key>
             <array>
               <string>DDA9.1</string>
             </array>
           </dict>
           <dict>
             <key>NSPrivacyAccessedAPIType</key>
             <string>NSPrivacyAccessedAPICategoryDiskSpace</string>
             <key>NSPrivacyAccessedAPITypeReasons</key>
             <array>
               <string>E174.1</string>
             </array>
           </dict>
         </array>
       </dict>
       </plist>
       ```

    ### API reference [#api-reference-1]

    * [`sharedEngine`](https://api-ref.agora.io/en/voice-sdk/ios/4.x/documentation/agorartckit/agorartcenginekit/sharedengine\(withappid\:delegate:\))

    * [`joinChannel`](https://api-ref.agora.io/en/voice-sdk/ios/4.x/documentation/agorartckit/agorartcenginekit/joinchannel\(bytoken\:channelid\:info\:uid\:joinsuccess:\))

    * [`leaveChannel`](https://api-ref.agora.io/en/voice-sdk/ios/4.x/documentation/agorartckit/agorartcenginekit/leavechannel\(_:\))

    * [`AgoraRtcEngineDelegate`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorartcenginedelegate)

    ### Frequently asked questions [#frequently-asked-questions-1]

    * [How can I listen for audience joining or leaving a channel?](/en/api-reference/faq/integration/audience_event)
    * [How can I solve channel-related issues?](/en/api-reference/faq/integration/channel)
    * [How can I set the log file?](/en/api-reference/faq/integration/set_log_file)
    * [How can I troubleshoot the issue of no sound?](/en/api-reference/faq/quality/audio_noaudio)
    * [What can I do if I get a pop-up warning saying 'the framework cannot be opened' when compiling an Xcode project?](/en/api-reference/faq/integration/framework_cannot_be_opened)
    * [How can I add a privacy manifest to my iOS app?](/en/api-reference/faq/other/ios_privacy_manifest)

    ### See also [#see-also-1]

    * [Error codes](reference/error-codes.mdx)

    * [Connection status management](build/manage-connection-and-quality/connection-status-management.mdx)

    
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
