# Connection basics (/en/realtime-media/rtm/build/connect-and-authenticate/connection/connection-management/ios)

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

A Signaling client must establish a connection with the server to access network resources. API calls, messages, and events in the channel are transmitted through network connections. While the Signaling SDK manages establishing and maintaining network connections, understanding its management mechanism helps you handle potential network errors in the app, and improves the end user experience.

    This page shows how to manage the connection between the Signaling SDK and the Signaling server.

    ## Prerequisites [#prerequisites-3]

    Ensure that you have integrated the Signaling SDK in your project and implemented the framework functionality from the [SDK quickstart](../../../quickstart) page.

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

    The Signaling SDK abstracts the complexities of managing network connections and provides the `didReceiveLinkStateEvent` event notifications to inform users of various connection status transitions. To efficiently handle connection errors and manage the connection state, implement a listener for this event.

    <CalloutContainer type="info">
      <CalloutDescription>
        The `didReceiveLinkStateEvent` event is only available in Signaling `2.2.1` and above.
      </CalloutDescription>
    </CalloutContainer>

    It is important to understand that Signaling provides two types of services: message services and stream services. The connection types corresponding to the two services are different. A client may use one or both of these services.

    ## Manage connections [#manage-connections-2]

    This section provides an overview of the types of Signaling connections. It discusses how to create a connection, monitor the connection state, configure timeout and reconnection behavior, and close connections when they are no longer needed.

    ### Create connection [#create-connection-2]

    When you create a Signaling client instance, or a stream channel instance, the Signaling SDK does not immediately establish a network connection with the server. At this point, you have just created an object instance for calling the Signaling SDK APIs. The client attempts to establish a network connection with the server only when you log in to the server.

    #### Message connection [#message-connection-6]

    The message service provides access to message channels, presence, storage, lock, token and other functionalities. The messages, data, and event notifications of these functionalities share this connection. A connection only exists within the client life cycle. It is established when you log in to the server, and ends when you call the `logout` method to log out of the server.

    To establish a `MESSAGE` connection, refer to the following code:

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objective-C">
          Objective-C
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        rtm.login(token) {res, error in
            if error != nil {
                print("\\(error?.operation) failed! error reason is \\(error?.reason)")
            } else {
                print("success")
            }
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objective-C">
        ```objc
        [rtm loginByToken:@"token" completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"login success!!");
            } else {
                NSLog(@"login failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <CalloutContainer type="info">
      <CalloutTitle>
        Info
      </CalloutTitle>

      <CalloutDescription>
        Establishing a `MESSAGE` connection increments the peak connections count.
      </CalloutDescription>
    </CalloutContainer>

    The `MESSAGE` connection is persistent. The client regularly sends heartbeat packets to the server to maintain activity. When you subscribe to multiple message channels, you receive all channel messages and event notifications through this connection.

    #### Stream connection [#stream-connection-6]

    The stream service provides users with all the capabilities of a stream channel. Unlike a `MESSAGE` connection, a `STREAM` connection is not globally unique. Each stream channel instance creates a separate connection. For example, when you create three stream channel instances and join the channels, the client establishes three network connections with the server.

    Before establishing a `STREAM` connection, you establish a `MESSAGE` connection. To do this, log in to the server. The life cycle of a `STREAM` connection starts when you join a channel and ends when you leave the channel. To establish a `STREAM` connection, refer to the following code:

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objective-C">
          Objective-C
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        let join_option = AgoraRtmJoinChannelOption();
        join_option.token = "your_token"
        join_option.features = [.presence, .storage]
        stream_channel.join(join_option, completion: { (res, error) in
            if error != nil {
                print("\\(error?.operation) failed! error reason is \\(error?.reason)")
            } else {
                print("success")
            }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objective-C">
        ```objc
        AgoraRtmJoinChannelOption* join_opt =  [[AgoraRtmJoinChannelOption alloc] init];
        join_opt.token =  @"yourToken";
        join_opt.features = Agora2RtmSubscribeChannelFeaturePresence | AgoraRtmSubscribeChannelFeatureMetadata;

        [stream_channel joinWithOption:join_opt completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"join channel success!!");
            } else {
                NSLog(@"join channel failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <CalloutContainer type="info">
      <CalloutTitle>
        Info
      </CalloutTitle>

      <CalloutDescription>
        Establishing a `STREAM` connection does not increase the peak connections count.
      </CalloutDescription>
    </CalloutContainer>

    A `STREAM` connection is also a persistent connection. The client sends heartbeat packets to the server at regular intervals to keep the connection active. The time interval for sending heartbeat packets is 0.5 seconds and cannot be changed. For application use-cases that require optimizing battery life, use of the stream service is not recommended.

    ### Monitoring the network connection [#monitoring-the-network-connection-2]

    The `didReceiveLinkStateEvent` callback provides event notifications for monitoring and managing the change of network connection status. By listening to this event, you can access crucial information such as:

    * The current state of the network connection.
    * The reasons or actions leading to the state change.
    * The previous network state before the change.
    * The timing of network connection state change.
    * The service type (message or stream) for which the connection status has changed.
    * Joined channels affected by the network state change.
    * Channels for which network connectivity was not successfully restored after the change.

    The detailed information provided by the event is valuable for handling business processes after recovering from unexpected disconnection states. It enables you to promptly identify and resolve issues caused by network connection changes, and ensures smooth business operations.

    To receive `didReceiveLinkStateEvent` notifications, refer to the following code:

    <Tabs defaultValue="swift" groupId="language">
      <TabsList>
        <TabsTrigger value="swift">
          Swift
        </TabsTrigger>

        <TabsTrigger value="objc">
          Objective-C
        </TabsTrigger>
      </TabsList>

      <TabsContent value="swift">
        ```swift
        class RtmListenerEx: NSObject, AgoraRtmClientDelegate {
            func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceiveLinkStateEvent event: AgoraRtmLinkStateEvent) {
                // code for handle link state change
            }
        }

        let listenerEx = RtmListenerEx()
        rtm.addDelegate(listenerEx)
        ```

        The following table describes the available event parameters:

        | Attribute            | Description                                                                                                                                                           |
        | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
        | `currentState`       | The current connection state.                                                                                                                                         |
        | `previousState`      | The previous connection state.                                                                                                                                        |
        | `serviceType`        | The service type. `message` or `stream`                                                                                                                               |
        | `operation`          | The operation that triggered this state change.                                                                                                                       |
        | `reason`             | Why the state change was triggered.                                                                                                                                   |
        | `affectedChannels`   | Channels affected by this state change.                                                                                                                               |
        | `unrestoredChannels` | Information about channels that have not been subscribed to or joined, including channel name, channel type, and temporary state data in the channel. Normally empty. |
        | `isResumed`          | Whether the connection state was restored from `disconnected` to `connected` within 2 minutes after the disconnection. `true` indicates that it has been restored.    |
        | `timestamp`          | The timestamp when this event notification was sent.                                                                                                                  |
      </TabsContent>

      <TabsContent value="objc">
        ```objc
        @interface RtmListenerEx : NSObject <AgoraRtmClientDelegate>
        @end

        @implementation RtmListenerEx
        - (void)rtmKit:(AgoraRtmClientKit *)rtmKit didReceiveLinkStateEvent:(AgoraRtmLinkStateEvent *)event {}
        @end

        RtmListenerEx* handlerEx =  [[RtmListenerEx alloc] init];

        [rtm addDelegate:handlerEx];
        ```

        The following table describes the available event parameters:

        | Attribute            | Description                                                                                                                                                                                          |
        | :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
        | `currentState`       | The current connection state.                                                                                                                                                                        |
        | `previousState`      | The previous connection state.                                                                                                                                                                       |
        | `serviceType`        | The service type. `AgoraRtmServiceTypeMessage` or `AgoraRtmServiceTypeStream`                                                                                                                        |
        | `operation`          | The operation that triggered this state change.                                                                                                                                                      |
        | `reason`             | Why the state change was triggered.                                                                                                                                                                  |
        | `affectedChannels`   | Channels affected by this state change.                                                                                                                                                              |
        | `unrestoredChannels` | Information about channels that have not been subscribed to or joined, including channel name, channel type, and temporary state data in the channel. Normally empty.                                |
        | `isResumed`          | Whether the connection state was restored from `AgoraRtmLinkStateDisconnected` to `AgoraRtmLinkStateConnected` within 2 minutes after the disconnection. `true` indicates that it has been restored. |
        | `timestamp`          | The timestamp when this event notification was sent.                                                                                                                                                 |
      </TabsContent>
    </Tabs>

    ### Heartbeat detection [#heartbeat-detection-2]

    Heartbeat detection helps Signaling clients and servers detect sudden network disconnections, such as client network disconnection or network switching, in a timely manner.

    #### Message connection [#message-connection-7]

    The Signaling client automatically sends a ping packet to the server every 5 seconds by default. If the server does not respond with a pong packet within 10 seconds, the client interprets it as a network issue. Consequently, the client's network connection status `AgoraRtmLinkState` switches from connected to disconnected, and it initiates reconnection attempts.

    Similarly, if the server does not receive a ping packet from the client within 15 seconds, it considers it a network problem on the client side. The server then waits for a predefined period `presenceTimeout`. If the client fails to reconnect during this period, the Signaling server sends the connection timeout event notifications to other users in the affected channel.

    It is important to note that the timeout mechanism applies only to accidental disconnections. Active disconnections, such as logging out by calling `logout`, do not trigger this behavior.

    The heartbeat interval is set to 5 seconds by default to balance device battery life and timely detection of network issues. However, you can customize this to increase battery life or to identify network disconnection in a more timely manner by specifying the `heartbeatInterval` parameter in `AgoraRtmClientConfig`. The supported range is \[5, 1800] seconds.

    While increasing `heartbeatInterval` saves battery, it delays detection of network issues and the peak connection count (PCU) may appear to be higher. Agora does not recommend setting the interval to the maximum value of 1800 seconds.

    To customize the heartbeat interval, refer to the following code:

    <Tabs defaultValue="swift" groupId="language">
      <TabsList>
        <TabsTrigger value="swift">
          Swift
        </TabsTrigger>

        <TabsTrigger value="objc">
          Objective-C
        </TabsTrigger>
      </TabsList>

      <TabsContent value="swift">
        ```swift
        let rtm_config = AgoraRtmClientConfig(appId: "yourAppId", userId: "uniqueUserId")
        rtm_config.heartbeatInterval = 30
        ```

        #### Stream connection [#stream-connection-7]

        When you create a stream channel instance and successfully join a stream channel, the client sends heartbeat packets to the server at 0.5 second intervals to keep the connection active. The stream connection heartbeat interval cannot be changed. For application use-cases that require increased battery life, Agora does not recommend use of stream connections.

        ### Heartbeat interval and presence timeout parameters [#heartbeat-interval-and-presence-timeout-parameters-2]

        `AgoraRtmClientConfig` includes two time interval parameters: `heartbeatInterval` and `presenceTimeout`. This section explains the two parameters and their relationship, to enable you to configure the Signaling client more effectively.

        1. `heartbeatInterval`: This parameter determines how often the client sends ping packets to the server to maintain an active connection. It affects the PCU count and is crucial for the SDK's global connection management functionality.

        2. `presenceTimeout`: If the client's ping packet is not received within the set `heartbeatInterval` plus a 10-second redundancy period, the server considers it a possible network connection failure. To account for the possibility that the client may be trying to fix the problem, the server delays broadcasting event notifications to other users in the channel for a period of time defined by the `presenceTimeout`. This approach helps avoid repeated notification of events as follows:

           * If the client successfully reconnects within the `presenceTimeout` period, the server does not send event notifications to other users, and the temporary user status set by the client is retained.

           * If the client fails to reconnect within the `presenceTimeout` period, the server broadcasts an `didReceivePresenceEvent` event notification with `remoteConnectionTimeout` to other users in the channel and clears the client's temporary user status. When the client successfully reconnects and resumes its subscription to the channel, the server broadcasts `remoteJoinChannel` event notifications to other users in the channel. If the client had previously set a temporary user state, it reapplies the cached temporary user state from the local session. Other users in the channel receive a corresponding `remoteStateChanged` event notification.

        For local users, departures triggered by presence timeout and departures resulting from calling `unsubscribe` are both considered channel leaving behaviors. The difference lies in the fact that presence timeout departures are typically caused by network connection failures. The local user may be in a `disconnected` or `suspended` state and attempting to reconnect. If the reconnection is successful, the channel subscription relationship and temporary user status are automatically restored. For other users in the channel, the user is identified as a rejoined user. An `unsubscribe` call is considered as a user actively leaving a channel. It prompts the client to clear the subscription relationship and the temporary user status for this channel.

        For example, suppose Client A sets the `heartbeatInterval` to 10 seconds and the `presenceTimeout` to 5 seconds. If Client A sends a ping packet but fails to receive a pong packet within 10 seconds, its connection state changes to `disconnected`. As Client A attempts to reconnect, the server waits for up to 25 seconds. If Client A fails to reconnect within this time, the server broadcasts a `didReceivePresenceEvent` notification of type `remoteConnectionTimeout` to other users in the channel. If Client A successfully restores the connection within 25 seconds, the server does not broadcast event notifications, and other users in the channel consider Client A to have never left the channel. In this example, it took the `heartbeatInterval` of 10 seconds, plus an error range of 10 seconds to allow for network delays, plus a `presenceTimeout` interval of 5 seconds from when Client A dropped offline to when other users were notified. The total time interval was 25 seconds.

        The `presenceTimeout` is crucial for handling situations where the network connection is quickly restored after being disconnected, such as network switching or driving through a tunnel. It prevents excessive event notifications and improves the user experience.
      </TabsContent>

      <TabsContent value="objc">
        ```objc
        AgoraRtmClientConfig* rtm_config = [[AgoraRtmClientConfig alloc] initWithAppId:@"your_appid" userId:@"your_userid"];
        rtm_config.heartbeatInterval = 30;
        ```

        #### Stream connection [#stream-connection-8]

        When you create a stream channel instance and successfully join a stream channel, the client sends heartbeat packets to the server at 0.5 second intervals to keep the connection active. The stream connection heartbeat interval cannot be changed. For application use-cases that require increased battery life, Agora does not recommend use of stream connections.

        ### Heartbeat interval and presence timeout parameters [#heartbeat-interval-and-presence-timeout-parameters-3]

        `AgoraRtmClientConfig` includes two time interval parameters: `heartbeatInterval` and `presenceTimeout`. This section explains the two parameters and their relationship, to enable you to configure the Signaling client more effectively.

        1. `heartbeatInterval`: This parameter determines how often the client sends ping packets to the server to maintain an active connection. It affects the PCU count and is crucial for the SDK's global connection management functionality.

        2. `presenceTimeout`: If the client's ping packet is not received within the set `heartbeatInterval` plus a 10-second redundancy period, the server considers it a possible network connection failure. To account for the possibility that the client may be trying to fix the problem, the server delays broadcasting event notifications to other users in the channel for a period of time defined by the `presenceTimeout`. This approach helps avoid repeated notification of events as follows:

           * If the client successfully reconnects within the `presenceTimeout` period, the server does not send event notifications to other users, and the temporary user status set by the client is retained.

           * If the client fails to reconnect within the `presenceTimeout` period, the server broadcasts an `didReceivePresenceEvent` event notification with `AgoraRtmPresenceEventTypeRemoteConnectionTimeout` to other users in the channel and clears the client's temporary user status. When the client successfully reconnects and resumes its subscription to the channel, the server broadcasts `AgoraRtmPresenceEventTypeRemoteJoinChannel` event notifications to other users in the channel. If the client had previously set a temporary user state, it reapplies the cached temporary user state from the local session. Other users in the channel receive a corresponding `AgoraRtmPresenceEventTypeRemoteStateChanged` event notification.

        For local users, departures triggered by presence timeout and departures resulting from calling `unsubscribeWithChannel` are both considered channel leaving behaviors. The difference lies in the fact that presence timeout departures are typically caused by network connection failures. The local user may be in a `AgoraRtmLinkStateDisconnected` or `AgoraRtmLinkStateSuspended` state and attempting to reconnect. If the reconnection is successful, the channel subscription relationship and temporary user status are automatically restored. For other users in the channel, the user is identified as a rejoined user. An `unsubscribeWithChannel` call is considered as a user actively leaving a channel. It prompts the client to clear the subscription relationship and the temporary user status for this channel.

        For example, suppose Client A sets the `heartbeatInterval` to 10 seconds and the `presenceTimeout` to 5 seconds. If Client A sends a ping packet but fails to receive a pong packet within 10 seconds, its connection state changes to `AgoraRtmLinkStateDisconnected`. As Client A attempts to reconnect, the server waits for up to 25 seconds. If Client A fails to reconnect within this time, the server broadcasts a `didReceivePresenceEvent` notification of type `AgoraRtmPresenceEventTypeRemoteConnectionTimeout` to other users in the channel. If Client A successfully restores the connection within 25 seconds, the server does not broadcast event notifications, and other users in the channel consider Client A to have never left the channel. In this example, it took the `heartbeatInterval` of 10 seconds, plus an error range of 10 seconds to allow for network delays, plus a `presenceTimeout` interval of 5 seconds from when Client A dropped offline to when other users were notified. The total time interval was 25 seconds.

        The `presenceTimeout` is crucial for handling situations where the network connection is quickly restored after being disconnected, such as network switching or driving through a tunnel. It prevents excessive event notifications and improves the user experience.
      </TabsContent>
    </Tabs>

    ### Close the connection [#close-the-connection-2]

    When a connection between the client and server is no longer required, call an appropriate method to close the connection.

    #### Message connection [#message-connection-8]

    In a message channel, the connection status affects measurement of the peak number of connections. Best practice is to actively disconnect by calling the `logout` method to log out of Signaling.

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objective-C">
          Objective-C
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        rtm.logout() {res, error in
            if error != nil {
                print("\\(error?.operation) failed! error reason is \\(error?.reason)")
            } else {
                print("success")
            }
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objective-C">
        ```objc
        [rtm logout:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"logout success!!");
            } else {
                NSLog(@"logout failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <CalloutContainer type="info">
      <CalloutTitle>
        Info
      </CalloutTitle>

      <CalloutDescription>
        When you call the `logout` method to disconnect, both `MESSAGE` and `STREAM` connections are terminated simultaneously. You exit all channels, and all cached data in the client is cleared.
      </CalloutDescription>
    </CalloutContainer>

    #### Stream connection [#stream-connection-9]

    When you no longer need a stream connection, call the `leave` method to exit the channel and disconnect the stream connection.

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objective-C">
          Objective-C
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        stream_channel.leave({ res, error in
            if error != nil {
                print("\\(error?.operation) failed! error reason is \\(error?.reason)")
            } else {
                print("success")
            }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objective-C">
        ```objc
        [stream_channel leave:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"leave channel success!!");
            } else {
                NSLog(@"leave channel failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ## 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.

    ### Integrate CallAPI [#integrate-callapi-1]

    `CallAPI` is a use-case-based solution designed for one-on-one calls that open in seconds. The API provides efficient call connection and image transmission functions that help you quickly set up video calls and transmit images during calls.

    This section guides you through integrating the `CallAPI` source code into your project and configuring the necessary project settings.

    #### Prerequisites [#prerequisites-4]

    Make sure you have:

    * [CocoaPods](https://guides.cocoapods.org/using/getting-started#getting-started)
    * [Xcode](https://apps.apple.com/cn/app/xcode/id497799835?mt=12) 12.0 or higher
    * A valid [Apple developer](https://developer.apple.com/) account

    #### Integration [#integration-1]

    Take the following steps to integrate `CallAPI` into your project:

    1. **Set up you project**

       If you don't have an Xcode project, [create a new Xcode project](https://help.apple.com/xcode/mac/current/#/dev07db0e578).

       In your project:

       1. [Set up automatic signing](https://help.apple.com/xcode/mac/current/#/dev23aab79b4)
       2. [Set the target devices](https://help.apple.com/xcode/mac/current/#/deve69552ee5) for deploying your app
       3. Open the file `info.plist` in the project navigator and edit the [property list](https://help.apple.com/xcode/mac/current/#/dev3f399a2a6) to add the recording and camera permissions required for the one-on-one private room:

          | Key                                    | Type   | Value                        |
          | -------------------------------------- | ------ | ---------------------------- |
          | Privacy - Microphone Usage Description | String | for one-on-one communication |
          | Privacy - Camera Usage Description     | String | for one-on-one communication |

    2. **Add dependencies**

       To add `CallAPI` to your project:

       1. Download or clone the [CallAPI source code repository](https://github.com/AgoraIO-Community/CallAPI/tree/main/iOS).

       2. Copy the `Call API` folder and the `CallAPI.podspec` file under `ios` into your project folder at the same level as `Podfile`.

       3. Include the following line in the `Podfile` to add a `CallAPI` dependency:

          ```ruby
          pod 'CallAPI', :path => 'CallAPI.podspec'
          ```

       4. Open a terminal and execute the following command to integrate the `CallAPI` code into your project:

          ```ruby
          pod install
          ```

    3. **Check the SDK version**

       If your project integrates Agora Video SDK or Signaling SDK, ensure that the integrated SDK version is compatible with `CallAPI`:

       * Video SDK: `4.1.1.26` or higher
       * Signaling SDK: `2.2.0` or higher

       If your current version is not compatible with `CallAPI`, modify the corresponding SDK version number in the `CallAPI.podspec` file.

       ```swift
       s.dependency 'AgoraRtcEngine_Special_iOS', '4.1.1.26'
       s.dependency 'AgoraRtm_iOS', '2.2.0'
       ```

    
  
      
  
      
  
      
  
      
  
