# Topics (/en/realtime-media/rtm/build/work-with-channels/topics)

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

<_PlatformTabsGroup groupMode="structured" canonicalPlatform="web" platforms="[&#x22;web&#x22;,&#x22;android&#x22;,&#x22;ios&#x22;,&#x22;macos&#x22;,&#x22;flutter&#x22;,&#x22;windows&#x22;,&#x22;linux-cpp&#x22;,&#x22;unity&#x22;]" showTabs="true">
  <_PlatformPanel platform="web">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />

    Topics are a mechanism for managing transmission and distribution of data in stream channels. After joining a stream channel, users cannot publish directly to the channel. Stream channels implement message sending and receiving through topics. All subscribers of a topic receive data sent by the publishers for the topic within 100 milliseconds.

    Compared to message channels, the topic mechanism in stream Channels has a higher message transmission rate, greater message concurrency, and synchronization capability with audio and video data transmission. Topics are therefore ideally suited to Metaverse, AR/VR, interactive games, real-time collaboration, and parallel control use-cases.

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

    In Signaling, a topic is a dynamic entity that you may use at any time without the need to explicitly create it in advance. The SDK automatically creates it when the first user joins a topic. Similarly, you don't need to explicitly destroy a topic. The SDK automatically destroys it when the last user leaves.

    The basic APIs for registering as a topic publisher or subscriber are join, leave, subscribe and unsubscribe.

    * **Message publisher**: When you join a topic, you register as a message publisher for the topic. This enables you to send messages to topic subscribers. When you leave a topic, you unregister as a message publisher, and lose the ability to publish messages.

    * **Message subscriber**: Subscribing to a topic enables you to receive messages that are published to the topic. To stop receiving topic messages, you unsubscribe from the topic.

    In topics, the publisher and subscriber roles are independent of each other. This means that a user may assume the role of a publisher, subscriber, or both. One role does not affect the behavior of the other.

    ## Prerequisites [#prerequisites]

    Ensure that you have:

    * Activated stream channel capability in [Agora Console](https://console.agora.io/). To do so, go to **Projects** > Edit project > **All features** > **Signaling** > **Stream channel configuration** and toggle to enable.

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../quickstart) page.

    * Implemented the functionality to create and join a stream channel. See [Stream channels](stream-channel)

    ## Implement topics in stream channels [#implement-topics-in-stream-channels]

    This section shows you how to join, leave, subscribe, and unsubscribe from topics.

    ### Join a topic [#join-a-topic]

    Joining a topic is a prerequisite for sending messages in a topic. Signaling allows a single client to join up to 8 topics in a channel concurrently.

    Use the following code to join a topic:

    ```js
    const topicName = "Chat";
    try {
        const result = await streamChannel.joinTopic(topicName);
    } catch (status) {
        const { operation, reason, errorCode } = status;
        console.log(`${operation} ErrorCode: ${errorCode}, due to: ${reason}.`);
    }
    ```

    After a user successfully joins a topic, the SDK triggers a `topic` event of type `REMOTE_JOIN`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Leave a topic [#leave-a-topic]

    When you no longer need to send messages to a topic, or the number of topics currently joined exceeds the limit, leave a topic to release resources.

    Refer to the following code to leave a topic:

    ```js
    const topicName = "Motion";
    try {
        const result = await streamChannel.leaveTopic(topicName);
    } catch (status) {
        const { operation, reason, errorCode } = status;
        console.log(`${operation} failed, ErrorCode ${errorCode}, due to: ${reason}.`);
    }
    ```

    After a user successfully leaves a topic, the SDK triggers a `topic` event of type `REMOTE_LEAVE`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Subscribe to a topic [#subscribe-to-a-topic]

    Joining a topic does not mean that you automatically start receiving messages sent to the topic. To receive messages, you subscribe to the topic and specify the message publishers to receive the messages from. In a single channel, you can subscribe to up to 50 topics concurrently, and in each topic, you can subscribe to up to 64 message publishers at the same time. The purpose of this limit is to balance end-side bandwidth and performance.

    To subscribe to message publishers in a topic, specify the message publishers list in the `users` property of the `options` object.

    Refer to the following code to subscribe to a topic:

    ```js
    const topicName = "Motion";
    const options = {
        users: ["Tony"],
    };
    try {
        const result = await streamChannel.subscribeTopic(topicName, options);
    } catch (status) {
        const { operation, reason, errorCode } = status;
        console.log(`${operation} failed, ErrorCode: ${errorCode}, due to: ${reason}.`);
    }
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * If you do not fill in the message publishers list when subscribing to a topic, up to 64 users are randomly subscribed by default. If there are less than 64 users in the topic, all users are subscribed.

        * If you make multiple topic subscription calls, where the first subscription message publisher list is `[UserA, UserB]` and the second publisher list is `[UserB, UserC]`, then the final subscription list is `[UserA, UserB, UserC]`.
      </CalloutDescription>
    </CalloutContainer>

    ### Unsubscribe from a topic [#unsubscribe-from-a-topic]

    To stop receiving messages from a particular message publisher in a topic, or to unfollow the entire topic, call `unsubscribeTopic`. If you do not fill in the `users` list when unsubscribing from a topic, you are unsubscribed from the entire topic.

    Refer to the following code to unsubscribe:

    ```js
    const topicName = "Motion";
    const options = {
        users: ["Tony"],
    };
    try {
        const result = await streamChannel.unsubscribeTopic(topicName, options);
    } catch (status) {
        const { operation, reason, errorCode } = status;
        console.log(`${operation} failed, ErrorCode: ${errorCode}, due to: ${reason}.`);
    }
    ```

    ### Add a topic event listener [#add-a-topic-event-listener]

    To set up an event listener for topic events, refer to [Add event listener](../send-and-receive-messages/add-event-listener). After successfully adding a topic event handler, you receive a `topic` event notifications for all topics in all stream channels you join or subscribe to. The event returns the following data:

    | Attribute     | Description                                                                                     |
    | ------------- | ----------------------------------------------------------------------------------------------- |
    | `eventType`   | Topic [event type](#topic-event-types)                                                          |
    | `channelName` | The name of the channel where the event occurred.                                               |
    | `publisher`   | The user ID that triggered this event.                                                          |
    | `topicInfos`  | Topic detailed information array, including topic name, topic publisher, and other information. |
    | `totalTopics` | The number of `topicInfos` arrays.                                                              |

    ### Topic event types [#topic-event-types]

    When a user in the channel joins or leaves a topic, Signaling triggers the `topic` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

    When a user joins a channel for the first time, the SDK delivers a topic event notification of type `SNAPSHOT` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

    | Attribute      | Description                                                                                                                                                                                                    |
    | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `SNAPSHOT`     | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
    | `REMOTE_JOIN`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
    | `REMOTE_LEAVE` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |

    ## Reference [#reference]

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

    ### Topic restrictions [#topic-restrictions]

    A single client can have an unlimited number of topics at the same time, and there is no limit on the number of subscribers and publishers in each topic. However, to optimize the client-side bandwidth and performance, there are certain limits on the following:

    * The number of topics that a single user can join concurrently.
    * The number of topics that a user can subscribe to concurrently.
    * The number of message publishers that can be concurrently subscribed to in a topic.

    For details, see [API usage restrictions](../../reference/limitations).

    ### Topic naming [#topic-naming]

    A topic name is a string of up to 16 letters or numbers in the ASCII character set. All topics under the same stream channel must have unique names. The same topic name in one stream channel is considered a single topic, but the same name in different stream channels is considered as two separate topics.

    The following characters in topic names are supported:

    * 26 lowercase English letters a-z
    * 26 uppercase English letters A-Z
    * 10 numbers 0-9
    * Space
    * `!`, `#`, \`

    , `%`, `&`, `(`, `)`, `+`, `,`, `-`, `:`, `;`, `<`, `=`, `>`, `?`, `@`, `[`, `]`, `^`, `_`, `{`, `|`, `}`, `~`, `` ` ``

    <CalloutContainer type="warning">
      <CalloutTitle>
        Important
      </CalloutTitle>

      <CalloutDescription>
        Topic names cannot begin with an underscore.
      </CalloutDescription>
    </CalloutContainer>

    The following characters are not supported:

    * `.`, `*`, `/`, `\`, `\0`
    * Non-printable ASCII characters

    Although the Signaling SDK does not require it, best practice when naming a topic is to use meaningful prefix characters to indicate the purpose of the topic or the type of messages published in it. Topic naming recommendations are similar to [Channel naming recommendations](../../reference/channel-naming).

    ### API reference [#api-reference]

    * [`joinTopic`](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform)
    * [`leaveTopic`](/en/api-reference/api-ref/signaling#topicleavepropsag_platform)
    * [`subscribeTopic`](/en/api-reference/api-ref/signaling#topicsubscribepropsag_platform)
    * [`unsubscribeTopic`](/en/api-reference/api-ref/signaling#topicunsubscribepropsag_platform)
    * [Event Listeners](/en/api-reference/api-ref/signaling#event-listeners)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="android">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />

    Topics are a mechanism for managing transmission and distribution of data in stream channels. After joining a stream channel, users cannot publish directly to the channel. Stream channels implement message sending and receiving through topics. All subscribers of a topic receive data sent by the publishers for the topic within 100 milliseconds.

    Compared to message channels, the topic mechanism in stream Channels has a higher message transmission rate, greater message concurrency, and synchronization capability with audio and video data transmission. Topics are therefore ideally suited to Metaverse, AR/VR, interactive games, real-time collaboration, and parallel control use-cases.

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

    In Signaling, a topic is a dynamic entity that you may use at any time without the need to explicitly create it in advance. The SDK automatically creates it when the first user joins a topic. Similarly, you don't need to explicitly destroy a topic. The SDK automatically destroys it when the last user leaves.

    The basic APIs for registering as a topic publisher or subscriber are join, leave, subscribe and unsubscribe.

    * **Message publisher**: When you join a topic, you register as a message publisher for the topic. This enables you to send messages to topic subscribers. When you leave a topic, you unregister as a message publisher, and lose the ability to publish messages.

    * **Message subscriber**: Subscribing to a topic enables you to receive messages that are published to the topic. To stop receiving topic messages, you unsubscribe from the topic.

    In topics, the publisher and subscriber roles are independent of each other. This means that a user may assume the role of a publisher, subscriber, or both. One role does not affect the behavior of the other.

    ## Prerequisites [#prerequisites-1]

    Ensure that you have:

    * Activated stream channel capability in [Agora Console](https://console.agora.io/). To do so, go to **Projects** > Edit project > **All features** > **Signaling** > **Stream channel configuration** and toggle to enable.

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../quickstart) page.

    * Implemented the functionality to create and join a stream channel. See [Stream channels](stream-channel)

    ## Implement topics in stream channels [#implement-topics-in-stream-channels-1]

    This section shows you how to join, leave, subscribe, and unsubscribe from topics.

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

    Joining a topic is a prerequisite for sending messages in a topic. Signaling allows a single client to join up to 8 topics in a channel concurrently.

    When joining a topic, set sending attributes using `options`. These parameters affect the sending behavior, such as whether the message order is preserved, the message priority, and synchronization of audio and video data transmitted on the same channel. For details, refer to the [API reference](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform).

    Use the following code to join a topic:

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

        <CodeBlockTabsTrigger value="Kotlin">
          Kotlin
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Java">
        ```java
        var options = new JoinTopicOptions();
        options.messageQos = RtmMessageQos.ORDERED;
        mStreamChannel.joinTopic("topic_name", options, new ResultCallback<Void>() {
            @Override
            public void onSuccess(Void responseInfo) {
                log(CALLBACK, "join topic success");
            }

            @Override
            public void onFailure(ErrorInfo errorInfo) {
                log(ERROR, errorInfo.toString());
            }
        });
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Kotlin">
        ```kotlin
        val options = JoinTopicOptions()
        options.messageQos = RtmMessageQos.ORDERED
        mStreamChannel.joinTopic("topic_name", options, object : ResultCallback<Void> {
            override fun onSuccess(responseInfo: Void?) {
                log(CALLBACK, "join topic success")
            }

            override fun onFailure(errorInfo: ErrorInfo) {
                log(ERROR, errorInfo.toString())
            }
        })
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    After a user successfully joins a topic, the SDK triggers an `onTopicEvent` of type `REMOTE_JOIN`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Leave a topic [#leave-a-topic-1]

    When you no longer need to send messages to a topic, or the number of topics currently joined exceeds the limit, leave a topic to release resources.

    Refer to the following code to leave a topic:

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

        <CodeBlockTabsTrigger value="Kotlin">
          Kotlin
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Java">
        ```java
        mStreamChannel.leaveTopic("Motion", new ResultCallback<Void>() {
            @Override
            public void onSuccess(Void responseInfo) {
                log(CALLBACK, "leave topic success");
            }

            @Override
            public void onFailure(ErrorInfo errorInfo) {
                log(ERROR, errorInfo.toString());
            }
        });
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Kotlin">
        ```kotlin
        mStreamChannel.leaveTopic("Motion", object : ResultCallback<Void> {
            override fun onSuccess(responseInfo: Void?) {
                log(CALLBACK, "leave topic success")
            }

            override fun onFailure(errorInfo: ErrorInfo) {
                log(ERROR, errorInfo.toString())
            }
        })
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    After a user successfully leaves a topic, the SDK triggers an `onTopicEvent` of type `REMOTE_LEAVE`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Subscribe to a topic [#subscribe-to-a-topic-1]

    Joining a topic does not mean that you automatically start receiving messages sent to the topic. To receive messages, you subscribe to the topic and specify the message publishers to receive the messages from. In a single channel, you can subscribe to up to 50 topics concurrently, and in each topic, you can subscribe to up to 64 message publishers at the same time. The purpose of this limit is to balance end-side bandwidth and performance.

    To subscribe to message publishers in a topic, specify the message publishers list in the `users` parameter of the `subscribeTopic` method.

    Refer to the following code to subscribe to a topic:

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

        <CodeBlockTabsTrigger value="Kotlin">
          Kotlin
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Java">
        ```java
        TopicOptions options = new TopicOptions();
        options.users = new ArrayList&lt;&gt;(Arrays.asList("user_name"));

        mStreamChannel.subscribeTopic("Motion", options, new ResultCallback<SubscribeTopicResult>() {
            @Override
            public void onSuccess(SubscribeTopicResult responseInfo) {
                log(CALLBACK, "subscribe topic success");
            }

            @Override
            public void onFailure(ErrorInfo errorInfo) {
                log(ERROR, errorInfo.toString());
            }
        });
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Kotlin">
        ```kotlin
        val options = TopicOptions()
        options.users = ArrayList(listOf("user_name"))

        mStreamChannel.subscribeTopic("Motion", options, object : ResultCallback<SubscribeTopicResult> {
            override fun onSuccess(responseInfo: SubscribeTopicResult?) {
                log(CALLBACK, "subscribe topic success")
            }

            override fun onFailure(errorInfo: ErrorInfo) {
                log(ERROR, errorInfo.toString())
            }
        })
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <CalloutContainer type="info">
      <CalloutDescription>
        * If you do not fill in the message publishers list when subscribing to a topic, up to 64 users are randomly subscribed by default. If there are less than 64 users in the topic, all users are subscribed.

        * If you make multiple topic subscription calls, where the first subscription message publisher list is `[UserA, UserB]` and the second publisher list is `[UserB, UserC]`, then the final subscription list is `[UserA, UserB, UserC]`.
      </CalloutDescription>
    </CalloutContainer>

    ### Unsubscribe from a topic [#unsubscribe-from-a-topic-1]

    To stop receiving messages from a particular message publisher in a topic, or to unfollow the entire topic, call `unsubscribeTopic`. If you do not fill in the message publishers list when unsubscribing from a topic, you are unsubscribed from the entire topic.

    Refer to the following code to unsubscribe:

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

        <CodeBlockTabsTrigger value="Kotlin">
          Kotlin
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Java">
        ```java
        var topicName = "Motion";
        var options = new TopicOptions();
        options.users = new ArrayList&lt;&gt;(Arrays.asList("Tony"));

        mStreamChannel.unsubscribeTopic(topicName.toString(), options, new ResultCallback<Void>() {
            @Override
            public void onSuccess(Void responseInfo) {
                log(CALLBACK, "unsubscribe topic success");
            }

            @Override
            public void onFailure(ErrorInfo errorInfo) {
                log(ERROR, errorInfo.toString());
            }
        });
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Kotlin">
        ```kotlin
        val topicName = "Motion"
        val options = TopicOptions()
        options.users = ArrayList(listOf("Tony"))

        mStreamChannel.unsubscribeTopic(topicName.toString(), options, object : ResultCallback<Void> {
            override fun onSuccess(responseInfo: Void?) {
                log(CALLBACK, "unsubscribe topic success")
            }

            override fun onFailure(errorInfo: ErrorInfo) {
                log(ERROR, errorInfo.toString())
            }
        })
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ### Add a topic event listener [#add-a-topic-event-listener-1]

    To set up an event listener for topic events, refer to [Add event listener](../send-and-receive-messages/add-event-listener). After successfully adding a topic event handler, you receive `onTopicEvent` notifications for all topics in all stream channels you join or subscribe to. The event returns the following data:

    | Attribute     | Description                                                                                     |
    | ------------- | ----------------------------------------------------------------------------------------------- |
    | `type`        | Topic [event type](#topic-event-types)                                                          |
    | `channelName` | The name of the channel where the event occurred.                                               |
    | `publisher`   | The user ID that triggered this event.                                                          |
    | `topicInfos`  | Topic detailed information array, including topic name, topic publisher, and other information. |
    | `timestamp`   | The timestamp when the event occurred.                                                          |

    ### Topic event types [#topic-event-types-1]

    When a user in the channel joins or leaves a topic, Signaling triggers the `onTopicEvent` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

    When a user joins a channel for the first time, the SDK delivers a topic event notification of type `SNAPSHOT` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

    | Attribute      | Description                                                                                                                                                                                                    |
    | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `SNAPSHOT`     | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
    | `REMOTE_JOIN`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
    | `REMOTE_LEAVE` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |

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

    ### Topic restrictions [#topic-restrictions-1]

    A single client can have an unlimited number of topics at the same time, and there is no limit on the number of subscribers and publishers in each topic. However, to optimize the client-side bandwidth and performance, there are certain limits on the following:

    * The number of topics that a single user can join concurrently.
    * The number of topics that a user can subscribe to concurrently.
    * The number of message publishers that can be concurrently subscribed to in a topic.

    For details, see [API usage restrictions](../../reference/limitations).

    ### Topic naming [#topic-naming-1]

    A topic name is a string of up to 16 letters or numbers in the ASCII character set. All topics under the same stream channel must have unique names. The same topic name in one stream channel is considered a single topic, but the same name in different stream channels is considered as two separate topics.

    The following characters in topic names are supported:

    * 26 lowercase English letters a-z
    * 26 uppercase English letters A-Z
    * 10 numbers 0-9
    * Space
    * `!`, `#`, \`

    , `%`, `&`, `(`, `)`, `+`, `,`, `-`, `:`, `;`, `<`, `=`, `>`, `?`, `@`, `[`, `]`, `^`, `_`, `{`, `|`, `}`, `~`, `` ` ``

    <CalloutContainer type="warning">
      <CalloutTitle>
        Important
      </CalloutTitle>

      <CalloutDescription>
        Topic names cannot begin with an underscore.
      </CalloutDescription>
    </CalloutContainer>

    The following characters are not supported:

    * `.`, `*`, `/`, `\`, `\0`
    * Non-printable ASCII characters

    Although the Signaling SDK does not require it, best practice when naming a topic is to use meaningful prefix characters to indicate the purpose of the topic or the type of messages published in it. Topic naming recommendations are similar to [Channel naming recommendations](../../reference/channel-naming).

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

    * [`joinTopic`](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform)
    * [`leaveTopic`](/en/api-reference/api-ref/signaling#topicleavepropsag_platform)
    * [`subscribeTopic`](/en/api-reference/api-ref/signaling#topicsubscribepropsag_platform)
    * [`unsubscribeTopic`](/en/api-reference/api-ref/signaling#topicunsubscribepropsag_platform)
    * [Event Listeners](/en/api-reference/api-ref/signaling#event-listeners)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="ios">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />

    Topics are a mechanism for managing transmission and distribution of data in stream channels. After joining a stream channel, users cannot publish directly to the channel. Stream channels implement message sending and receiving through topics. All subscribers of a topic receive data sent by the publishers for the topic within 100 milliseconds.

    Compared to message channels, the topic mechanism in stream Channels has a higher message transmission rate, greater message concurrency, and synchronization capability with audio and video data transmission. Topics are therefore ideally suited to Metaverse, AR/VR, interactive games, real-time collaboration, and parallel control use-cases.

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

    In Signaling, a topic is a dynamic entity that you may use at any time without the need to explicitly create it in advance. The SDK automatically creates it when the first user joins a topic. Similarly, you don't need to explicitly destroy a topic. The SDK automatically destroys it when the last user leaves.

    The basic APIs for registering as a topic publisher or subscriber are join, leave, subscribe and unsubscribe.

    * **Message publisher**: When you join a topic, you register as a message publisher for the topic. This enables you to send messages to topic subscribers. When you leave a topic, you unregister as a message publisher, and lose the ability to publish messages.

    * **Message subscriber**: Subscribing to a topic enables you to receive messages that are published to the topic. To stop receiving topic messages, you unsubscribe from the topic.

    In topics, the publisher and subscriber roles are independent of each other. This means that a user may assume the role of a publisher, subscriber, or both. One role does not affect the behavior of the other.

    ## Prerequisites [#prerequisites-2]

    Ensure that you have:

    * Activated stream channel capability in [Agora Console](https://console.agora.io/). To do so, go to **Projects** > Edit project > **All features** > **Signaling** > **Stream channel configuration** and toggle to enable.

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../quickstart) page.

    * Implemented the functionality to create and join a stream channel. See [Stream channels](stream-channel)

    ## Implement topics in stream channels [#implement-topics-in-stream-channels-2]

    This section shows you how to join, leave, subscribe, and unsubscribe from topics.

    ### Join a topic [#join-a-topic-2]

    Joining a topic is a prerequisite for sending messages in a topic. Signaling allows a single client to join up to 8 topics in a channel concurrently.

    When joining a topic, set sending attributes using `option`. These parameters affect the sending behavior, such as whether the message order is preserved, the message priority, and synchronization of audio and video data transmitted on the same channel. For details, refer to the [API reference](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform).

    Use the following code to join a topic:

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

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

      <TabsContent value="swift">
        ```swift
        streamChannel.joinTopic("your_topic", option: nil) { response, errorInfo in
            if errorInfo == nil {
                print("Join topic success!!")
            } else {
                print("Join topic failed, errorCode: \\(errorInfo?.errorCode), reason: \\(errorInfo?.reason)")
            }
        }
        ```

        After a user successfully joins a topic, the SDK triggers a `didReceiveTopicEvent` of type `remoteJoinTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.
      </TabsContent>

      <TabsContent value="objc">
        ```objc
        [stream_channel joinTopic:@"your_topic" option:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"join topic success!!");
            } else {
                NSLog(@"join topic failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);

            }
        }];
        ```

        After a user successfully joins a topic, the SDK triggers a `didReceiveTopicEvent` of type `AgoraRtmTopicEventTypeRemoteJoinTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.
      </TabsContent>
    </Tabs>

    ### Leave a topic [#leave-a-topic-2]

    When you no longer need to send messages to a topic, or the number of topics currently joined exceeds the limit, leave a topic to release resources.

    Refer to the following code to leave a topic:

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

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

      <TabsContent value="swift">
        ```swift
        streamChannel.leaveTopic("your_topic", completion: { response, errorInfo in
            if errorInfo == nil {
                    print("Leave topic success!!")
                } else {
                    if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
                        print("Leave topic failed, errorCode: \\(errorCode), reason: \\(reason)")
                    }
                }
        })
        ```

        After a user successfully leaves a topic, the SDK triggers a `didReceiveTopicEvent` of type `remoteLeaveTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.
      </TabsContent>

      <TabsContent value="objc">
        ```objc
        [stream_channel leaveTopic:@"your_topic" completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"leave topic success!!");
            } else {
                NSLog(@"leave topic failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);

            }
        }];
        ```

        After a user successfully leaves a topic, the SDK triggers a `didReceiveTopicEvent` of type `AgoraRtmTopicEventTypeRemoteLeaveTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.
      </TabsContent>
    </Tabs>

    ### Subscribe to a topic [#subscribe-to-a-topic-2]

    Joining a topic does not mean that you automatically start receiving messages sent to the topic. To receive messages, you subscribe to the topic and specify the message publishers to receive the messages from. In a single channel, you can subscribe to up to 50 topics concurrently, and in each topic, you can subscribe to up to 64 message publishers at the same time. The purpose of this limit is to balance end-side bandwidth and performance.

    To subscribe to message publishers in a topic, specify the message publishers list in the `users` parameter of the `AgoraRtmTopicOption` instance.

    Refer to the following code to subscribe to a topic:

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

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

      <CodeBlockTab value="Swift">
        ```swift
        let topicOption = AgoraRtmTopicOption()
        topicOption.users = ["user1", "user2"]

        streamChannel.subscribeTopic("your_topic", option: topicOption,completion: { response, errorInfo in
        if errorInfo == nil {
            print("Subscribe topic success!!")
        } else {
            if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
                let successUsers = response?.succeedUsers ?? []
                let failedUsers = response?.failedUsers ?? []
                print("Subscribe topic failed, errorCode: \\(errorCode), reason: \\(reason)")
                print("Succeeded users: \\(successUsers), Failed users: \\(failedUsers)")
            }
        }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objective-C">
        ```objc
        AgoraRtmTopicOption* topic_opt = [[AgoraRtmTopicOption alloc] init];
        topic_opt.users = @[@"user1", @"user2"];

        [stream_channel subscribeTopic:@"your_topic" withOption:topic_opt completion:^(AgoraRtmTopicSubscriptionResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"subscribe topic success!!");
            } else {
                NSArray<NSString *> *success_users = response.succeedUsers;
                NSArray<NSString *> *fail_users = response.failedUsers;
                NSLog(@"subscribe topic failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <CalloutContainer type="info">
      <CalloutDescription>
        * If you do not fill in the message publishers list when subscribing to a topic, up to 64 users are randomly subscribed by default. If there are less than 64 users in the topic, all users are subscribed.

        * If you make multiple topic subscription calls, where the first subscription message publisher list is `[UserA, UserB]` and the second publisher list is `[UserB, UserC]`, then the final subscription list is `[UserA, UserB, UserC]`.
      </CalloutDescription>
    </CalloutContainer>

    ### Unsubscribe from a topic [#unsubscribe-from-a-topic-2]

    To stop receiving messages from a particular message publisher in a topic, or to unfollow the entire topic, call `unsubscribeTopic`. If you do not fill in the message publishers list when unsubscribing from a topic, you are unsubscribed from the entire topic.

    Refer to the following code to unsubscribe:

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

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

      <TabsContent value="swift">
        ```swift
        let topicOpt = AgoraRtmTopicOption()
        topicOpt.users = ["user1", "user2"]

        streamChannel.unsubscribeTopic("your_topic", option: topicOpt,  completion:{ response, errorInfo in
            if errorInfo == nil {
                print("Unsubscribe topic success!!")
            } else {
                if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
                    print("Unsubscribe topic failed, errorCode: \\(errorCode), reason: \\(reason)")
                }
            }
        });
        ```

        ### Topic event types [#topic-event-types-2]

        When a user in the channel joins or leaves a topic, Signaling triggers the `didReceiveTopicEvent` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

        When a user joins a channel for the first time, the SDK delivers a topic event notification of type `snapshot` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

        | Attribute          | Description                                                                                                                                                                                                    |
        | ------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
        | `snapshot`         | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
        | `remoteJoinTopic`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
        | `remoteLeaveTopic` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |
      </TabsContent>

      <TabsContent value="objc">
        ```objc
        AgoraRtmTopicOption* topic_opt = [[AgoraRtmTopicOption alloc] init];
        topic_opt.users = @[@"user1", @"user2"];

        [stream_channel unsubscribeTopic:@"your_topic" withOption:topic_opt completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"subscribe topic success!!");
            } else {
                NSLog(@"subscribe topic failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);

            }
        }];
        ```

        #### Topic event types [#topic-event-types-3]

        When a user in the channel joins or leaves a topic, Signaling triggers the `didReceiveTopicEvent` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

        When a user joins a channel for the first time, the SDK delivers a topic event notification of type `AgoraRtmStorageEventTypeSnapshot` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

        | Attribute                                | Description                                                                                                                                                                                                    |
        | ---------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
        | `AgoraRtmStorageEventTypeSnapshot`       | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
        | `AgoraRtmTopicEventTypeRemoteJoinTopic`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
        | `AgoraRtmTopicEventTypeRemoteLeaveTopic` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |
      </TabsContent>
    </Tabs>

    ### Add a topic event listener [#add-a-topic-event-listener-2]

    To set up an event listener for topic events, refer to [Add event listener](../send-and-receive-messages/add-event-listener). After successfully adding a topic event handler, you receive `didReceiveTopicEvent` notifications for all topics in all stream channels you join or subscribe to. The event returns the following data:

    | Attribute     | Description                                                                                     |
    | ------------- | :---------------------------------------------------------------------------------------------- |
    | `type`        | Topic [event type](#topic-event-types)                                                          |
    | `channelName` | The name of the channel where the event occurred.                                               |
    | `publisher`   | The user ID that triggered this event.                                                          |
    | `topicInfos`  | Topic detailed information array, including topic name, topic publisher, and other information. |
    | `timestamp`   | The timestamp when the event occurred.                                                          |

    ## Reference [#reference-2]

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

    ### Topic restrictions [#topic-restrictions-2]

    A single client can have an unlimited number of topics at the same time, and there is no limit on the number of subscribers and publishers in each topic. However, to optimize the client-side bandwidth and performance, there are certain limits on the following:

    * The number of topics that a single user can join concurrently.
    * The number of topics that a user can subscribe to concurrently.
    * The number of message publishers that can be concurrently subscribed to in a topic.

    For details, see [API usage restrictions](../../reference/limitations).

    ### Topic naming [#topic-naming-2]

    A topic name is a string of up to 16 letters or numbers in the ASCII character set. All topics under the same stream channel must have unique names. The same topic name in one stream channel is considered a single topic, but the same name in different stream channels is considered as two separate topics.

    The following characters in topic names are supported:

    * 26 lowercase English letters a-z
    * 26 uppercase English letters A-Z
    * 10 numbers 0-9
    * Space
    * `!`, `#`, \`

    , `%`, `&`, `(`, `)`, `+`, `,`, `-`, `:`, `;`, `<`, `=`, `>`, `?`, `@`, `[`, `]`, `^`, `_`, `{`, `|`, `}`, `~`, `` ` ``

    <CalloutContainer type="warning">
      <CalloutTitle>
        Important
      </CalloutTitle>

      <CalloutDescription>
        Topic names cannot begin with an underscore.
      </CalloutDescription>
    </CalloutContainer>

    The following characters are not supported:

    * `.`, `*`, `/`, `\`, `\0`
    * Non-printable ASCII characters

    Although the Signaling SDK does not require it, best practice when naming a topic is to use meaningful prefix characters to indicate the purpose of the topic or the type of messages published in it. Topic naming recommendations are similar to [Channel naming recommendations](../../reference/channel-naming).

    ### API reference [#api-reference-2]

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

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

      <TabsContent value="swift">
        * [`joinTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#topicjoinpropsag_platform)
        * [`leaveTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#topicleavepropsag_platform)
        * [`subscribeTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#topicsubscribepropsag_platform)
        * [`unsubscribeTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#topicunsubscribepropsag_platform)
        * [Event Listeners](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#event-listeners)
      </TabsContent>

      <TabsContent value="objc">
        * [`joinTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#topicjoinpropsag_platform)
        * [`leaveTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#topicleavepropsag_platform)
        * [`subscribeTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#topicsubscribepropsag_platform)
        * [`unsubscribeTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#topicunsubscribepropsag_platform)
        * [Event Listeners](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#event-listeners)
      </TabsContent>
    </Tabs>

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="macos">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="macos" />

    Topics are a mechanism for managing transmission and distribution of data in stream channels. After joining a stream channel, users cannot publish directly to the channel. Stream channels implement message sending and receiving through topics. All subscribers of a topic receive data sent by the publishers for the topic within 100 milliseconds.

    Compared to message channels, the topic mechanism in stream Channels has a higher message transmission rate, greater message concurrency, and synchronization capability with audio and video data transmission. Topics are therefore ideally suited to Metaverse, AR/VR, interactive games, real-time collaboration, and parallel control use-cases.

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

    In Signaling, a topic is a dynamic entity that you may use at any time without the need to explicitly create it in advance. The SDK automatically creates it when the first user joins a topic. Similarly, you don't need to explicitly destroy a topic. The SDK automatically destroys it when the last user leaves.

    The basic APIs for registering as a topic publisher or subscriber are join, leave, subscribe and unsubscribe.

    * **Message publisher**: When you join a topic, you register as a message publisher for the topic. This enables you to send messages to topic subscribers. When you leave a topic, you unregister as a message publisher, and lose the ability to publish messages.

    * **Message subscriber**: Subscribing to a topic enables you to receive messages that are published to the topic. To stop receiving topic messages, you unsubscribe from the topic.

    In topics, the publisher and subscriber roles are independent of each other. This means that a user may assume the role of a publisher, subscriber, or both. One role does not affect the behavior of the other.

    ## Prerequisites [#prerequisites-3]

    Ensure that you have:

    * Activated stream channel capability in [Agora Console](https://console.agora.io/). To do so, go to **Projects** > Edit project > **All features** > **Signaling** > **Stream channel configuration** and toggle to enable.

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../quickstart) page.

    * Implemented the functionality to create and join a stream channel. See [Stream channels](stream-channel)

    ## Implement topics in stream channels [#implement-topics-in-stream-channels-3]

    This section shows you how to join, leave, subscribe, and unsubscribe from topics.

    ### Join a topic [#join-a-topic-3]

    Joining a topic is a prerequisite for sending messages in a topic. Signaling allows a single client to join up to 8 topics in a channel concurrently.

    When joining a topic, set sending attributes using `option`. These parameters affect the sending behavior, such as whether the message order is preserved, the message priority, and synchronization of audio and video data transmitted on the same channel. For details, refer to the [API reference](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform).

    Use the following code to join a topic:

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

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

      <TabsContent value="swift">
        ```swift
        streamChannel.joinTopic("your_topic", option: nil) { response, errorInfo in
            if errorInfo == nil {
                print("Join topic success!!")
            } else {
                print("Join topic failed, errorCode: \\(errorInfo?.errorCode), reason: \\(errorInfo?.reason)")
            }
        }
        ```

        After a user successfully joins a topic, the SDK triggers a `didReceiveTopicEvent` of type `remoteJoinTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.
      </TabsContent>

      <TabsContent value="objc">
        ```objc
        [stream_channel joinTopic:@"your_topic" option:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"join topic success!!");
            } else {
                NSLog(@"join topic failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);

            }
        }];
        ```

        After a user successfully joins a topic, the SDK triggers a `didReceiveTopicEvent` of type `AgoraRtmTopicEventTypeRemoteJoinTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.
      </TabsContent>
    </Tabs>

    ### Leave a topic [#leave-a-topic-3]

    When you no longer need to send messages to a topic, or the number of topics currently joined exceeds the limit, leave a topic to release resources.

    Refer to the following code to leave a topic:

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

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

      <TabsContent value="swift">
        ```swift
        streamChannel.leaveTopic("your_topic", completion: { response, errorInfo in
            if errorInfo == nil {
                    print("Leave topic success!!")
                } else {
                    if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
                        print("Leave topic failed, errorCode: \\(errorCode), reason: \\(reason)")
                    }
                }
        })
        ```

        After a user successfully leaves a topic, the SDK triggers a `didReceiveTopicEvent` of type `remoteLeaveTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.
      </TabsContent>

      <TabsContent value="objc">
        ```objc
        [stream_channel leaveTopic:@"your_topic" completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"leave topic success!!");
            } else {
                NSLog(@"leave topic failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);

            }
        }];
        ```

        After a user successfully leaves a topic, the SDK triggers a `didReceiveTopicEvent` of type `AgoraRtmTopicEventTypeRemoteLeaveTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.
      </TabsContent>
    </Tabs>

    ### Subscribe to a topic [#subscribe-to-a-topic-3]

    Joining a topic does not mean that you automatically start receiving messages sent to the topic. To receive messages, you subscribe to the topic and specify the message publishers to receive the messages from. In a single channel, you can subscribe to up to 50 topics concurrently, and in each topic, you can subscribe to up to 64 message publishers at the same time. The purpose of this limit is to balance end-side bandwidth and performance.

    To subscribe to message publishers in a topic, specify the message publishers list in the `users` parameter of the `AgoraRtmTopicOption` instance.

    Refer to the following code to subscribe to a topic:

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

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

      <CodeBlockTab value="Swift">
        ```swift
        let topicOption = AgoraRtmTopicOption()
        topicOption.users = ["user1", "user2"]

        streamChannel.subscribeTopic("your_topic", option: topicOption,completion: { response, errorInfo in
        if errorInfo == nil {
            print("Subscribe topic success!!")
        } else {
            if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
                let successUsers = response?.succeedUsers ?? []
                let failedUsers = response?.failedUsers ?? []
                print("Subscribe topic failed, errorCode: \\(errorCode), reason: \\(reason)")
                print("Succeeded users: \\(successUsers), Failed users: \\(failedUsers)")
            }
        }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objective-C">
        ```objc
        AgoraRtmTopicOption* topic_opt = [[AgoraRtmTopicOption alloc] init];
        topic_opt.users = @[@"user1", @"user2"];

        [stream_channel subscribeTopic:@"your_topic" withOption:topic_opt completion:^(AgoraRtmTopicSubscriptionResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"subscribe topic success!!");
            } else {
                NSArray<NSString *> *success_users = response.succeedUsers;
                NSArray<NSString *> *fail_users = response.failedUsers;
                NSLog(@"subscribe topic failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <CalloutContainer type="info">
      <CalloutDescription>
        * If you do not fill in the message publishers list when subscribing to a topic, up to 64 users are randomly subscribed by default. If there are less than 64 users in the topic, all users are subscribed.

        * If you make multiple topic subscription calls, where the first subscription message publisher list is `[UserA, UserB]` and the second publisher list is `[UserB, UserC]`, then the final subscription list is `[UserA, UserB, UserC]`.
      </CalloutDescription>
    </CalloutContainer>

    ### Unsubscribe from a topic [#unsubscribe-from-a-topic-3]

    To stop receiving messages from a particular message publisher in a topic, or to unfollow the entire topic, call `unsubscribeTopic`. If you do not fill in the message publishers list when unsubscribing from a topic, you are unsubscribed from the entire topic.

    Refer to the following code to unsubscribe:

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

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

      <TabsContent value="swift">
        ```swift
        let topicOpt = AgoraRtmTopicOption()
        topicOpt.users = ["user1", "user2"]

        streamChannel.unsubscribeTopic("your_topic", option: topicOpt,  completion:{ response, errorInfo in
            if errorInfo == nil {
                print("Unsubscribe topic success!!")
            } else {
                if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
                    print("Unsubscribe topic failed, errorCode: \\(errorCode), reason: \\(reason)")
                }
            }
        });
        ```

        ### Topic event types [#topic-event-types-4]

        When a user in the channel joins or leaves a topic, Signaling triggers the `didReceiveTopicEvent` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

        When a user joins a channel for the first time, the SDK delivers a topic event notification of type `snapshot` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

        | Attribute          | Description                                                                                                                                                                                                    |
        | ------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
        | `snapshot`         | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
        | `remoteJoinTopic`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
        | `remoteLeaveTopic` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |
      </TabsContent>

      <TabsContent value="objc">
        ```objc
        AgoraRtmTopicOption* topic_opt = [[AgoraRtmTopicOption alloc] init];
        topic_opt.users = @[@"user1", @"user2"];

        [stream_channel unsubscribeTopic:@"your_topic" withOption:topic_opt completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"subscribe topic success!!");
            } else {
                NSLog(@"subscribe topic failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);

            }
        }];
        ```

        #### Topic event types [#topic-event-types-5]

        When a user in the channel joins or leaves a topic, Signaling triggers the `didReceiveTopicEvent` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

        When a user joins a channel for the first time, the SDK delivers a topic event notification of type `AgoraRtmStorageEventTypeSnapshot` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

        | Attribute                                | Description                                                                                                                                                                                                    |
        | ---------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
        | `AgoraRtmStorageEventTypeSnapshot`       | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
        | `AgoraRtmTopicEventTypeRemoteJoinTopic`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
        | `AgoraRtmTopicEventTypeRemoteLeaveTopic` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |
      </TabsContent>
    </Tabs>

    ### Add a topic event listener [#add-a-topic-event-listener-3]

    To set up an event listener for topic events, refer to [Add event listener](../send-and-receive-messages/add-event-listener). After successfully adding a topic event handler, you receive `didReceiveTopicEvent` notifications for all topics in all stream channels you join or subscribe to. The event returns the following data:

    | Attribute     | Description                                                                                     |
    | ------------- | :---------------------------------------------------------------------------------------------- |
    | `type`        | Topic [event type](#topic-event-types)                                                          |
    | `channelName` | The name of the channel where the event occurred.                                               |
    | `publisher`   | The user ID that triggered this event.                                                          |
    | `topicInfos`  | Topic detailed information array, including topic name, topic publisher, and other information. |
    | `timestamp`   | The timestamp when the event occurred.                                                          |

    ## Reference [#reference-3]

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

    ### Topic restrictions [#topic-restrictions-3]

    A single client can have an unlimited number of topics at the same time, and there is no limit on the number of subscribers and publishers in each topic. However, to optimize the client-side bandwidth and performance, there are certain limits on the following:

    * The number of topics that a single user can join concurrently.
    * The number of topics that a user can subscribe to concurrently.
    * The number of message publishers that can be concurrently subscribed to in a topic.

    For details, see [API usage restrictions](../../reference/limitations).

    ### Topic naming [#topic-naming-3]

    A topic name is a string of up to 16 letters or numbers in the ASCII character set. All topics under the same stream channel must have unique names. The same topic name in one stream channel is considered a single topic, but the same name in different stream channels is considered as two separate topics.

    The following characters in topic names are supported:

    * 26 lowercase English letters a-z
    * 26 uppercase English letters A-Z
    * 10 numbers 0-9
    * Space
    * `!`, `#`, \`

    , `%`, `&`, `(`, `)`, `+`, `,`, `-`, `:`, `;`, `<`, `=`, `>`, `?`, `@`, `[`, `]`, `^`, `_`, `{`, `|`, `}`, `~`, `` ` ``

    <CalloutContainer type="warning">
      <CalloutTitle>
        Important
      </CalloutTitle>

      <CalloutDescription>
        Topic names cannot begin with an underscore.
      </CalloutDescription>
    </CalloutContainer>

    The following characters are not supported:

    * `.`, `*`, `/`, `\`, `\0`
    * Non-printable ASCII characters

    Although the Signaling SDK does not require it, best practice when naming a topic is to use meaningful prefix characters to indicate the purpose of the topic or the type of messages published in it. Topic naming recommendations are similar to [Channel naming recommendations](../../reference/channel-naming).

    ### API reference [#api-reference-3]

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

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

      <TabsContent value="swift">
        * [`joinTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#topicjoinpropsag_platform)
        * [`leaveTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#topicleavepropsag_platform)
        * [`subscribeTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#topicsubscribepropsag_platform)
        * [`unsubscribeTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#topicunsubscribepropsag_platform)
        * [Event Listeners](/en/api-reference/api-ref/signaling?platform=ios\&tab=swift#event-listeners)
      </TabsContent>

      <TabsContent value="objc">
        * [`joinTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#topicjoinpropsag_platform)
        * [`leaveTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#topicleavepropsag_platform)
        * [`subscribeTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#topicsubscribepropsag_platform)
        * [`unsubscribeTopic`](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#topicunsubscribepropsag_platform)
        * [Event Listeners](/en/api-reference/api-ref/signaling?platform=ios\&tab=objc#event-listeners)
      </TabsContent>
    </Tabs>

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="flutter">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />

    Topics are a mechanism for managing transmission and distribution of data in stream channels. After joining a stream channel, users cannot publish directly to the channel. Stream channels implement message sending and receiving through topics. All subscribers of a topic receive data sent by the publishers for the topic within 100 milliseconds.

    Compared to message channels, the topic mechanism in stream Channels has a higher message transmission rate, greater message concurrency, and synchronization capability with audio and video data transmission. Topics are therefore ideally suited to Metaverse, AR/VR, interactive games, real-time collaboration, and parallel control use-cases.

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

    In Signaling, a topic is a dynamic entity that you may use at any time without the need to explicitly create it in advance. The SDK automatically creates it when the first user joins a topic. Similarly, you don't need to explicitly destroy a topic. The SDK automatically destroys it when the last user leaves.

    The basic APIs for registering as a topic publisher or subscriber are join, leave, subscribe and unsubscribe.

    * **Message publisher**: When you join a topic, you register as a message publisher for the topic. This enables you to send messages to topic subscribers. When you leave a topic, you unregister as a message publisher, and lose the ability to publish messages.

    * **Message subscriber**: Subscribing to a topic enables you to receive messages that are published to the topic. To stop receiving topic messages, you unsubscribe from the topic.

    In topics, the publisher and subscriber roles are independent of each other. This means that a user may assume the role of a publisher, subscriber, or both. One role does not affect the behavior of the other.

    ## Prerequisites [#prerequisites-4]

    Ensure that you have:

    * Activated stream channel capability in [Agora Console](https://console.agora.io/). To do so, go to **Projects** > Edit project > **All features** > **Signaling** > **Stream channel configuration** and toggle to enable.

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../quickstart) page.

    * Implemented the functionality to create and join a stream channel. See [Stream channels](stream-channel)

    ## Implement topics in stream channels [#implement-topics-in-stream-channels-4]

    This section shows you how to join, leave, subscribe, and unsubscribe from topics.

    ### Join a topic [#join-a-topic-4]

    Joining a topic is a prerequisite for sending messages in a topic. Signaling allows a single client to join up to 8 topics in a channel concurrently.

    When joining a topic, set sending attributes using `options`. These parameters affect the sending behavior, such as whether the message order is preserved, the message priority, and synchronization of audio and video data transmitted on the same channel. For details, refer to the [API reference](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform).

    Use the following code to join a topic:

    ```dart
    final topicName = 'topic101';
    try {
        var (status, response) = await stChannel.joinTopic(
            topicName,
            priority: RtmMessagePriority.normal,
            syncWithMedia: false);
        if (status.error == true ){
           print('${status.operation} failed, errorCode: ${status.errorCode}, due to ${status.reason}');
        } else {
            print('${status.operation} success!');
        }
    } catch (e) {
        print('something went wrong: $e');
    }
    ```

    After a user successfully joins a topic, the SDK triggers a `topic` event of type `remoteJoinTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Leave a topic [#leave-a-topic-4]

    When you no longer need to send messages to a topic, or the number of topics currently joined exceeds the limit, leave a topic to release resources.

    Refer to the following code to leave a topic:

    ```dart
    final topicName = 'topic101';
    try {
        var (status, response) = await stChannel.leaveTopic( topicName);
        if (status.error == true ){
           print('${status.operation} failed, errorCode: ${status.errorCode}, due to ${status.reason}');
        } else {
            print('${status.operation} success!');
        }
    } catch (e) {
        print('something went wrong: $e');
    }
    ```

    After a user successfully leaves a topic, the SDK triggers a `topic` event of type `remoteLeaveTopic`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Subscribe to a topic [#subscribe-to-a-topic-4]

    Joining a topic does not mean that you automatically start receiving messages sent to the topic. To receive messages, you subscribe to the topic and specify the message publishers to receive the messages from. In a single channel, you can subscribe to up to 50 topics concurrently, and in each topic, you can subscribe to up to 64 message publishers at the same time. The purpose of this limit is to balance end-side bandwidth and performance.

    To subscribe to message publishers in a topic, specify the message publishers list in the `users` parameter of the `subscribeTopic` method.

    Refer to the following code to subscribe to a topic:

    ```dart
    final topicName = 'topic101';
    var users = ['Tony','Lily'];
    try {
        var (status, response) = await stChannel.subscribeTopic(
            topicName,
            users: users );
        if (status.error == true ){
           print('${status.operation} failed, errorCode: ${status.errorCode}, due to ${status.reason}');
        } else {
            print('${status.operation} success!');
        }
    } catch (e) {
        print('something went wrong: $e');
    }
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * If you do not fill in the message publishers list when subscribing to a topic, up to 64 users are randomly subscribed by default. If there are less than 64 users in the topic, all users are subscribed.

        * If you make multiple topic subscription calls, where the first subscription message publisher list is `[UserA, UserB]` and the second publisher list is `[UserB, UserC]`, then the final subscription list is `[UserA, UserB, UserC]`.
      </CalloutDescription>
    </CalloutContainer>

    ### Unsubscribe from a topic [#unsubscribe-from-a-topic-4]

    To stop receiving messages from a particular message publisher in a topic, or to unfollow the entire topic, call `unsubscribeTopic`. If you do not fill in the message publishers list when unsubscribing from a topic, you are unsubscribed from the entire topic.

    Refer to the following code to unsubscribe:

    ```dart
    final topicName = 'topic101';
    var users = ['Tony','Lily'];
    try {
        var (status, response) = await stChannel.unsubscribeTopic(
            topicName,
            users: users );
        if (status.error == true ){
           print('${status.operation} failed, errorCode: ${status.errorCode}, due to ${status.reason}');
        } else {
            print('${status.operation} success!');
        }
    } catch (e) {
        print('something went wrong: $e');
    }
    ```

    ### Add a topic event listener [#add-a-topic-event-listener-4]

    To set up an event listener for topic events, refer to [Add event listener](../send-and-receive-messages/add-event-listener). After successfully adding a topic event handler, you receive `topic` notifications for all topics in all stream channels you join or subscribe to. The event returns the following data:

    | Attribute     | Description                                                                            |
    | ------------- | -------------------------------------------------------------------------------------- |
    | `type`        | Topic [event type](#topic-event-types)                                                 |
    | `channelName` | The name of the channel where the event occurred.                                      |
    | `publisher`   | The user ID that triggered this event.                                                 |
    | `topicInfos`  | Topic information array, including topic name, topic publisher, and other information. |
    | `timestamp`   | The timestamp when the event occurred.                                                 |

    ### Topic event types [#topic-event-types-6]

    When a user in the channel joins or leaves a topic, Signaling triggers the `topic` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

    When a user joins a channel for the first time, the SDK delivers a topic event notification of type `snapshot` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

    | Attribute          | Description                                                                                                                                                                                                    |
    | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `snapshot`         | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
    | `remoteJoinTopic`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
    | `remoteLeaveTopic` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |

    ## Reference [#reference-4]

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

    ### Topic restrictions [#topic-restrictions-4]

    A single client can have an unlimited number of topics at the same time, and there is no limit on the number of subscribers and publishers in each topic. However, to optimize the client-side bandwidth and performance, there are certain limits on the following:

    * The number of topics that a single user can join concurrently.
    * The number of topics that a user can subscribe to concurrently.
    * The number of message publishers that can be concurrently subscribed to in a topic.

    For details, see [API usage restrictions](../../reference/limitations).

    ### Topic naming [#topic-naming-4]

    A topic name is a string of up to 16 letters or numbers in the ASCII character set. All topics under the same stream channel must have unique names. The same topic name in one stream channel is considered a single topic, but the same name in different stream channels is considered as two separate topics.

    The following characters in topic names are supported:

    * 26 lowercase English letters a-z
    * 26 uppercase English letters A-Z
    * 10 numbers 0-9
    * Space
    * `!`, `#`, \`

    , `%`, `&`, `(`, `)`, `+`, `,`, `-`, `:`, `;`, `<`, `=`, `>`, `?`, `@`, `[`, `]`, `^`, `_`, `{`, `|`, `}`, `~`, `` ` ``

    <CalloutContainer type="warning">
      <CalloutTitle>
        Important
      </CalloutTitle>

      <CalloutDescription>
        Topic names cannot begin with an underscore.
      </CalloutDescription>
    </CalloutContainer>

    The following characters are not supported:

    * `.`, `*`, `/`, `\`, `\0`
    * Non-printable ASCII characters

    Although the Signaling SDK does not require it, best practice when naming a topic is to use meaningful prefix characters to indicate the purpose of the topic or the type of messages published in it. Topic naming recommendations are similar to [Channel naming recommendations](../../reference/channel-naming).

    ### API reference [#api-reference-4]

    * [`joinTopic`](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform)
    * [`leaveTopic`](/en/api-reference/api-ref/signaling#topicleavepropsag_platform)
    * [`subscribeTopic`](/en/api-reference/api-ref/signaling#topicsubscribepropsag_platform)
    * [`unsubscribeTopic`](/en/api-reference/api-ref/signaling#topicunsubscribepropsag_platform)
    * [Event Listeners](/en/api-reference/api-ref/signaling#event-listeners)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="windows">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="windows" />

    Topics are a mechanism for managing transmission and distribution of data in stream channels. After joining a stream channel, users cannot publish directly to the channel. Stream channels implement message sending and receiving through topics. All subscribers of a topic receive data sent by the publishers for the topic within 100 milliseconds.

    Compared to message channels, the topic mechanism in stream Channels has a higher message transmission rate, greater message concurrency, and synchronization capability with audio and video data transmission. Topics are therefore ideally suited to Metaverse, AR/VR, interactive games, real-time collaboration, and parallel control use-cases.

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

    In Signaling, a topic is a dynamic entity that you may use at any time without the need to explicitly create it in advance. The SDK automatically creates it when the first user joins a topic. Similarly, you don't need to explicitly destroy a topic. The SDK automatically destroys it when the last user leaves.

    The basic APIs for registering as a topic publisher or subscriber are join, leave, subscribe and unsubscribe.

    * **Message publisher**: When you join a topic, you register as a message publisher for the topic. This enables you to send messages to topic subscribers. When you leave a topic, you unregister as a message publisher, and lose the ability to publish messages.

    * **Message subscriber**: Subscribing to a topic enables you to receive messages that are published to the topic. To stop receiving topic messages, you unsubscribe from the topic.

    In topics, the publisher and subscriber roles are independent of each other. This means that a user may assume the role of a publisher, subscriber, or both. One role does not affect the behavior of the other.

    ## Prerequisites [#prerequisites-5]

    Ensure that you have:

    * Activated stream channel capability in [Agora Console](https://console.agora.io/). To do so, go to **Projects** > Edit project > **All features** > **Signaling** > **Stream channel configuration** and toggle to enable.

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../quickstart) page.

    * Implemented the functionality to create and join a stream channel. See [Stream channels](stream-channel)

    ## Implement topics in stream channels [#implement-topics-in-stream-channels-5]

    This section shows you how to join, leave, subscribe, and unsubscribe from topics.

    ### Join a topic [#join-a-topic-5]

    Joining a topic is a prerequisite for sending messages in a topic. Signaling allows a single client to join up to 8 topics in a channel concurrently.

    When joining a topic, set sending attributes using `options`. These parameters affect the sending behavior, such as whether the message order is preserved, the message priority, and synchronization of audio and video data transmitted on the same channel. For details, refer to the [API reference](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform).

    Use the following code to join a topic:

    ```cpp
    JoinTopicOptions options;
    uint64_t requestId;
    streamChannel->joinTopic("topicName", options, requestId);
    ```

    After you call this method, the SDK triggers the `onJoinTopicResult` callback and returns the call result.

    ```cpp
    // Asynchronous callback
    class RtmEventHandler : public IRtmEventHandler {
        void onJoinTopicResult(const uint64_t requestId, const char *channelName, const char *userId, const char *topic, const char *meta, RTM_ERROR_CODE errorCode) override {
            if (errorCode != RTM_ERROR_OK) {
                printf("JoinTopic failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
            } else {
                printf("JoinTopic success\n");
            }
        }
    }
    ```

    After a user successfully joins a topic, the SDK triggers an `onTopicEvent` of type `RTM_TOPIC_EVENT_TYPE_REMOTE_JOIN_TOPIC`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Leave a topic [#leave-a-topic-5]

    When you no longer need to send messages to a topic, or the number of topics currently joined exceeds the limit, leave a topic to release resources.

    Refer to the following code to leave a topic:

    ```cpp
    uint64_t requestId;
    streamChannel->leaveTopic("topicName", requestId);
    ```

    After you call this method, the SDK triggers the `onLeaveTopicResult` callback and returns the call result.

    ```cpp
    // Asynchronous callback
    class RtmEventHandler : public IRtmEventHandler {
        void onLeaveTopicResult(const uint64_t requestId, const char *channelName, const char *userId, const char *topic, const char *meta, RTM_ERROR_CODE errorCode) override
        {
            if (errorCode != RTM_ERROR_OK) {
                printf("LeaveTopic failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
            } else {
                printf("LeaveTopic success\n");
            }
        }
    };
    ```

    After a user successfully leaves a topic, the SDK triggers an `onTopicEvent` of type `RTM_TOPIC_EVENT_TYPE_REMOTE_LEAVE_TOPIC`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Subscribe to a topic [#subscribe-to-a-topic-5]

    Joining a topic does not mean that you automatically start receiving messages sent to the topic. To receive messages, you subscribe to the topic and specify the message publishers to receive the messages from. In a single channel, you can subscribe to up to 50 topics concurrently, and in each topic, you can subscribe to up to 64 message publishers at the same time. The purpose of this limit is to balance end-side bandwidth and performance.

    To subscribe to message publishers in a topic, specify the message publishers list in the `users` parameter of the `TopicOptions` instance.

    Refer to the following code to subscribe to a topic:

    ```cpp
    std::vector<const char*> users;
    users.push_back("UserA");
    users.push_back("UserB");

    TopicOptions options;
    options.users = users.data();
    options.userCount = users.size();

    uint64_t requestId;
    streamChannel->subscribeTopic("topicName", options, requestId);
    ```

    After you call this method, the SDK triggers the `onSubscribeTopicResult` callback and returns the call result.

    ```cpp
    // Asynchronous callback
    class RtmEventHandler : public IRtmEventHandler {
        void onSubscribeTopicResult(const uint64_t requestId, const char *channelName, const char *userId, const char *topic, UserList succeedUsers, UserList failedUsers, RTM_ERROR_CODE errorCode) override {
            if (errorCode != RTM_ERROR_OK) {
                printf("SubscribeTopic failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
            } else {
                printf("SubscribeTopic success\n");
            }
        }
    };
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * If you do not fill in the message publishers list when subscribing to a topic, up to 64 users are randomly subscribed by default. If there are less than 64 users in the topic, all users are subscribed.

        * If you make multiple topic subscription calls, where the first subscription message publisher list is `[UserA, UserB]` and the second publisher list is `[UserB, UserC]`, then the final subscription list is `[UserA, UserB, UserC]`.
      </CalloutDescription>
    </CalloutContainer>

    ### Unsubscribe from a topic [#unsubscribe-from-a-topic-5]

    To stop receiving messages from a particular message publisher in a topic, or to unfollow the entire topic, call `unsubscribeTopic`. If you do not fill in the message publishers list when unsubscribing from a topic, you are unsubscribed from the entire topic.

    Refer to the following code to unsubscribe:

    ```cpp
    std::vector<const char*> users;
    users.push_back("UserA");
    users.push_back("UserB");
    TopicOptions options;
    options.users = users.data();
    options.userCount = users.size();

    uint64_t requestId;
    streamChannel->unsubscribeTopic("topicName", options, requestId);
    ```

    After you call this method, the SDK triggers the `onUnsubscribeTopicResult` callback and returns the call result.

    ```cpp
    // Asynchronous callback
    class RtmEventHandler : public IRtmEventHandler {
        void onUnsubscribeTopicResult(const uint64_t requestId, const char* channelName, const char* topic, RTM_ERROR_CODE errorCode) override {
            if (errorCode != RTM_ERROR_OK) {
                printf("UnsubscribeTopic failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
            } else {
                printf("UnsubscribeTopic success\n");
            }
        }
    };
    ```

    ### Add a topic event listener [#add-a-topic-event-listener-5]

    To set up an event listener for topic events, refer to [Add event listener](../send-and-receive-messages/add-event-listener). After successfully adding a topic event handler, you receive `onTopicEvent` notifications for all topics in all stream channels you join or subscribe to. The event returns the following data:

    | Attribute        | Description                                                                                     |
    | ---------------- | ----------------------------------------------------------------------------------------------- |
    | `type`           | Topic [event type](#topic-event-types)                                                          |
    | `channelName`    | The name of the channel where the event occurred.                                               |
    | `publisher`      | The user ID that triggered this event.                                                          |
    | `topicInfos`     | Topic detailed information array, including topic name, topic publisher, and other information. |
    | `topicInfoCount` | The number of `topicInfos`.                                                                     |
    | `timestamp`      | The timestamp of when the event occurred.                                                       |

    ### Topic event types [#topic-event-types-7]

    When a user in the channel joins or leaves a topic, Signaling triggers the `onTopicEvent` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

    When a user joins a channel for the first time, the SDK delivers a topic event notification of type `RTM_TOPIC_EVENT_TYPE_SNAPSHOT` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

    | Attribute                                 | Description                                                                                                                                                                                                    |
    | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `RTM_TOPIC_EVENT_TYPE_SNAPSHOT`           | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
    | `RTM_TOPIC_EVENT_TYPE_REMOTE_JOIN_TOPIC`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
    | `RTM_TOPIC_EVENT_TYPE_REMOTE_LEAVE_TOPIC` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |

    ## Reference [#reference-5]

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

    ### Topic restrictions [#topic-restrictions-5]

    A single client can have an unlimited number of topics at the same time, and there is no limit on the number of subscribers and publishers in each topic. However, to optimize the client-side bandwidth and performance, there are certain limits on the following:

    * The number of topics that a single user can join concurrently.
    * The number of topics that a user can subscribe to concurrently.
    * The number of message publishers that can be concurrently subscribed to in a topic.

    For details, see [API usage restrictions](../../reference/limitations).

    ### Topic naming [#topic-naming-5]

    A topic name is a string of up to 16 letters or numbers in the ASCII character set. All topics under the same stream channel must have unique names. The same topic name in one stream channel is considered a single topic, but the same name in different stream channels is considered as two separate topics.

    The following characters in topic names are supported:

    * 26 lowercase English letters a-z
    * 26 uppercase English letters A-Z
    * 10 numbers 0-9
    * Space
    * `!`, `#`, \`

    , `%`, `&`, `(`, `)`, `+`, `,`, `-`, `:`, `;`, `<`, `=`, `>`, `?`, `@`, `[`, `]`, `^`, `_`, `{`, `|`, `}`, `~`, `` ` ``

    <CalloutContainer type="warning">
      <CalloutTitle>
        Important
      </CalloutTitle>

      <CalloutDescription>
        Topic names cannot begin with an underscore.
      </CalloutDescription>
    </CalloutContainer>

    The following characters are not supported:

    * `.`, `*`, `/`, `\`, `\0`
    * Non-printable ASCII characters

    Although the Signaling SDK does not require it, best practice when naming a topic is to use meaningful prefix characters to indicate the purpose of the topic or the type of messages published in it. Topic naming recommendations are similar to [Channel naming recommendations](../../reference/channel-naming).

    ### API reference [#api-reference-5]

    * [`joinTopic`](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform)
    * [`leaveTopic`](/en/api-reference/api-ref/signaling#topicleavepropsag_platform)
    * [`subscribeTopic`](/en/api-reference/api-ref/signaling#topicsubscribepropsag_platform)
    * [`unsubscribeTopic`](/en/api-reference/api-ref/signaling#topicunsubscribepropsag_platform)
    * [Event Listeners](/en/api-reference/api-ref/signaling#event-listeners)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="linux-cpp">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="linux-cpp" />

    Topics are a mechanism for managing transmission and distribution of data in stream channels. After joining a stream channel, users cannot publish directly to the channel. Stream channels implement message sending and receiving through topics. All subscribers of a topic receive data sent by the publishers for the topic within 100 milliseconds.

    Compared to message channels, the topic mechanism in stream Channels has a higher message transmission rate, greater message concurrency, and synchronization capability with audio and video data transmission. Topics are therefore ideally suited to Metaverse, AR/VR, interactive games, real-time collaboration, and parallel control use-cases.

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

    In Signaling, a topic is a dynamic entity that you may use at any time without the need to explicitly create it in advance. The SDK automatically creates it when the first user joins a topic. Similarly, you don't need to explicitly destroy a topic. The SDK automatically destroys it when the last user leaves.

    The basic APIs for registering as a topic publisher or subscriber are join, leave, subscribe and unsubscribe.

    * **Message publisher**: When you join a topic, you register as a message publisher for the topic. This enables you to send messages to topic subscribers. When you leave a topic, you unregister as a message publisher, and lose the ability to publish messages.

    * **Message subscriber**: Subscribing to a topic enables you to receive messages that are published to the topic. To stop receiving topic messages, you unsubscribe from the topic.

    In topics, the publisher and subscriber roles are independent of each other. This means that a user may assume the role of a publisher, subscriber, or both. One role does not affect the behavior of the other.

    ## Prerequisites [#prerequisites-6]

    Ensure that you have:

    * Activated stream channel capability in [Agora Console](https://console.agora.io/). To do so, go to **Projects** > Edit project > **All features** > **Signaling** > **Stream channel configuration** and toggle to enable.

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../quickstart) page.

    * Implemented the functionality to create and join a stream channel. See [Stream channels](stream-channel)

    ## Implement topics in stream channels [#implement-topics-in-stream-channels-6]

    This section shows you how to join, leave, subscribe, and unsubscribe from topics.

    ### Join a topic [#join-a-topic-6]

    Joining a topic is a prerequisite for sending messages in a topic. Signaling allows a single client to join up to 8 topics in a channel concurrently.

    When joining a topic, set sending attributes using `options`. These parameters affect the sending behavior, such as whether the message order is preserved, the message priority, and synchronization of audio and video data transmitted on the same channel. For details, refer to the [API reference](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform).

    Use the following code to join a topic:

    ```cpp
    JoinTopicOptions options;
    uint64_t requestId;
    streamChannel->joinTopic("topicName", options, requestId);
    ```

    After you call this method, the SDK triggers the `onJoinTopicResult` callback and returns the call result.

    ```cpp
    // Asynchronous callback
    class RtmEventHandler : public IRtmEventHandler {
        void onJoinTopicResult(const uint64_t requestId, const char *channelName, const char *userId, const char *topic, const char *meta, RTM_ERROR_CODE errorCode) override {
            if (errorCode != RTM_ERROR_OK) {
                printf("JoinTopic failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
            } else {
                printf("JoinTopic success\n");
            }
        }
    }
    ```

    After a user successfully joins a topic, the SDK triggers an `onTopicEvent` of type `RTM_TOPIC_EVENT_TYPE_REMOTE_JOIN_TOPIC`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Leave a topic [#leave-a-topic-6]

    When you no longer need to send messages to a topic, or the number of topics currently joined exceeds the limit, leave a topic to release resources.

    Refer to the following code to leave a topic:

    ```cpp
    uint64_t requestId;
    streamChannel->leaveTopic("topicName", requestId);
    ```

    After you call this method, the SDK triggers the `onLeaveTopicResult` callback and returns the call result.

    ```cpp
    // Asynchronous callback
    class RtmEventHandler : public IRtmEventHandler {
        void onLeaveTopicResult(const uint64_t requestId, const char *channelName, const char *userId, const char *topic, const char *meta, RTM_ERROR_CODE errorCode) override
        {
            if (errorCode != RTM_ERROR_OK) {
                printf("LeaveTopic failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
            } else {
                printf("LeaveTopic success\n");
            }
        }
    };
    ```

    After a user successfully leaves a topic, the SDK triggers an `onTopicEvent` of type `RTM_TOPIC_EVENT_TYPE_REMOTE_LEAVE_TOPIC`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Subscribe to a topic [#subscribe-to-a-topic-6]

    Joining a topic does not mean that you automatically start receiving messages sent to the topic. To receive messages, you subscribe to the topic and specify the message publishers to receive the messages from. In a single channel, you can subscribe to up to 50 topics concurrently, and in each topic, you can subscribe to up to 64 message publishers at the same time. The purpose of this limit is to balance end-side bandwidth and performance.

    To subscribe to message publishers in a topic, specify the message publishers list in the `users` parameter of the `TopicOptions` instance.

    Refer to the following code to subscribe to a topic:

    ```cpp
    std::vector<const char*> users;
    users.push_back("UserA");
    users.push_back("UserB");

    TopicOptions options;
    options.users = users.data();
    options.userCount = users.size();

    uint64_t requestId;
    streamChannel->subscribeTopic("topicName", options, requestId);
    ```

    After you call this method, the SDK triggers the `onSubscribeTopicResult` callback and returns the call result.

    ```cpp
    // Asynchronous callback
    class RtmEventHandler : public IRtmEventHandler {
        void onSubscribeTopicResult(const uint64_t requestId, const char *channelName, const char *userId, const char *topic, UserList succeedUsers, UserList failedUsers, RTM_ERROR_CODE errorCode) override {
            if (errorCode != RTM_ERROR_OK) {
                printf("SubscribeTopic failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
            } else {
                printf("SubscribeTopic success\n");
            }
        }
    };
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * If you do not fill in the message publishers list when subscribing to a topic, up to 64 users are randomly subscribed by default. If there are less than 64 users in the topic, all users are subscribed.

        * If you make multiple topic subscription calls, where the first subscription message publisher list is `[UserA, UserB]` and the second publisher list is `[UserB, UserC]`, then the final subscription list is `[UserA, UserB, UserC]`.
      </CalloutDescription>
    </CalloutContainer>

    ### Unsubscribe from a topic [#unsubscribe-from-a-topic-6]

    To stop receiving messages from a particular message publisher in a topic, or to unfollow the entire topic, call `unsubscribeTopic`. If you do not fill in the message publishers list when unsubscribing from a topic, you are unsubscribed from the entire topic.

    Refer to the following code to unsubscribe:

    ```cpp
    std::vector<const char*> users;
    users.push_back("UserA");
    users.push_back("UserB");
    TopicOptions options;
    options.users = users.data();
    options.userCount = users.size();

    uint64_t requestId;
    streamChannel->unsubscribeTopic("topicName", options, requestId);
    ```

    After you call this method, the SDK triggers the `onUnsubscribeTopicResult` callback and returns the call result.

    ```cpp
    // Asynchronous callback
    class RtmEventHandler : public IRtmEventHandler {
        void onUnsubscribeTopicResult(const uint64_t requestId, const char* channelName, const char* topic, RTM_ERROR_CODE errorCode) override {
            if (errorCode != RTM_ERROR_OK) {
                printf("UnsubscribeTopic failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
            } else {
                printf("UnsubscribeTopic success\n");
            }
        }
    };
    ```

    ### Add a topic event listener [#add-a-topic-event-listener-6]

    To set up an event listener for topic events, refer to [Add event listener](../send-and-receive-messages/add-event-listener). After successfully adding a topic event handler, you receive `onTopicEvent` notifications for all topics in all stream channels you join or subscribe to. The event returns the following data:

    | Attribute        | Description                                                                                     |
    | ---------------- | ----------------------------------------------------------------------------------------------- |
    | `type`           | Topic [event type](#topic-event-types)                                                          |
    | `channelName`    | The name of the channel where the event occurred.                                               |
    | `publisher`      | The user ID that triggered this event.                                                          |
    | `topicInfos`     | Topic detailed information array, including topic name, topic publisher, and other information. |
    | `topicInfoCount` | The number of `topicInfos`.                                                                     |
    | `timestamp`      | The timestamp of when the event occurred.                                                       |

    ### Topic event types [#topic-event-types-8]

    When a user in the channel joins or leaves a topic, Signaling triggers the `onTopicEvent` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

    When a user joins a channel for the first time, the SDK delivers a topic event notification of type `RTM_TOPIC_EVENT_TYPE_SNAPSHOT` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

    | Attribute                                 | Description                                                                                                                                                                                                    |
    | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `RTM_TOPIC_EVENT_TYPE_SNAPSHOT`           | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
    | `RTM_TOPIC_EVENT_TYPE_REMOTE_JOIN_TOPIC`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
    | `RTM_TOPIC_EVENT_TYPE_REMOTE_LEAVE_TOPIC` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |

    ## Reference [#reference-6]

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

    ### Topic restrictions [#topic-restrictions-6]

    A single client can have an unlimited number of topics at the same time, and there is no limit on the number of subscribers and publishers in each topic. However, to optimize the client-side bandwidth and performance, there are certain limits on the following:

    * The number of topics that a single user can join concurrently.
    * The number of topics that a user can subscribe to concurrently.
    * The number of message publishers that can be concurrently subscribed to in a topic.

    For details, see [API usage restrictions](../../reference/limitations).

    ### Topic naming [#topic-naming-6]

    A topic name is a string of up to 16 letters or numbers in the ASCII character set. All topics under the same stream channel must have unique names. The same topic name in one stream channel is considered a single topic, but the same name in different stream channels is considered as two separate topics.

    The following characters in topic names are supported:

    * 26 lowercase English letters a-z
    * 26 uppercase English letters A-Z
    * 10 numbers 0-9
    * Space
    * `!`, `#`, \`

    , `%`, `&`, `(`, `)`, `+`, `,`, `-`, `:`, `;`, `<`, `=`, `>`, `?`, `@`, `[`, `]`, `^`, `_`, `{`, `|`, `}`, `~`, `` ` ``

    <CalloutContainer type="warning">
      <CalloutTitle>
        Important
      </CalloutTitle>

      <CalloutDescription>
        Topic names cannot begin with an underscore.
      </CalloutDescription>
    </CalloutContainer>

    The following characters are not supported:

    * `.`, `*`, `/`, `\`, `\0`
    * Non-printable ASCII characters

    Although the Signaling SDK does not require it, best practice when naming a topic is to use meaningful prefix characters to indicate the purpose of the topic or the type of messages published in it. Topic naming recommendations are similar to [Channel naming recommendations](../../reference/channel-naming).

    ### API reference [#api-reference-6]

    * [`joinTopic`](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform)
    * [`leaveTopic`](/en/api-reference/api-ref/signaling#topicleavepropsag_platform)
    * [`subscribeTopic`](/en/api-reference/api-ref/signaling#topicsubscribepropsag_platform)
    * [`unsubscribeTopic`](/en/api-reference/api-ref/signaling#topicunsubscribepropsag_platform)
    * [Event Listeners](/en/api-reference/api-ref/signaling#event-listeners)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="unity">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />

    Topics are a mechanism for managing transmission and distribution of data in stream channels. After joining a stream channel, users cannot publish directly to the channel. Stream channels implement message sending and receiving through topics. All subscribers of a topic receive data sent by the publishers for the topic within 100 milliseconds.

    Compared to message channels, the topic mechanism in stream Channels has a higher message transmission rate, greater message concurrency, and synchronization capability with audio and video data transmission. Topics are therefore ideally suited to Metaverse, AR/VR, interactive games, real-time collaboration, and parallel control use-cases.

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

    In Signaling, a topic is a dynamic entity that you may use at any time without the need to explicitly create it in advance. The SDK automatically creates it when the first user joins a topic. Similarly, you don't need to explicitly destroy a topic. The SDK automatically destroys it when the last user leaves.

    The basic APIs for registering as a topic publisher or subscriber are join, leave, subscribe and unsubscribe.

    * **Message publisher**: When you join a topic, you register as a message publisher for the topic. This enables you to send messages to topic subscribers. When you leave a topic, you unregister as a message publisher, and lose the ability to publish messages.

    * **Message subscriber**: Subscribing to a topic enables you to receive messages that are published to the topic. To stop receiving topic messages, you unsubscribe from the topic.

    In topics, the publisher and subscriber roles are independent of each other. This means that a user may assume the role of a publisher, subscriber, or both. One role does not affect the behavior of the other.

    ## Prerequisites [#prerequisites-7]

    Ensure that you have:

    * Activated stream channel capability in [Agora Console](https://console.agora.io/). To do so, go to **Projects** > Edit project > **All features** > **Signaling** > **Stream channel configuration** and toggle to enable.

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../quickstart) page.

    * Implemented the functionality to create and join a stream channel. See [Stream channels](stream-channel)

    ## Implement topics in stream channels [#implement-topics-in-stream-channels-7]

    This section shows you how to join, leave, subscribe, and unsubscribe from topics.

    ### Join a topic [#join-a-topic-7]

    Joining a topic is a prerequisite for sending messages in a topic. Signaling allows a single client to join up to 8 topics in a channel concurrently.

    When joining a topic, set sending attributes using `options`. These parameters affect the sending behavior, such as whether the message order is preserved, the message priority, and synchronization of audio and video data transmitted on the same channel. For details, refer to the [API reference](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform).

    Use the following code to join a topic:

    ```java
    string topic = "Chat";
    var options = new JoinTopicOptions();
    options.qos = RTM_MESSAGE_QOS.ORDERED;

    var (status,response) = await streamChannel.JoinTopicAsync(topic, options);
    if (status.Error)
    {
        Debug.Log(string.Format("{0} failed. ErrorCode: {1}, due to {2}", status.Operation, status.ErrorCode, status.Reason));
    }
    else
    {
        Debug.Log(string.Format("User {0} joined topic {1} successfully at channel {2}", response.UserId, response.Topic, response.ChannelName));
    }
    ```

    After a user successfully joins a topic, the SDK triggers an `OnTopicEvent` of type `REMOTE_JOIN`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Leave a topic [#leave-a-topic-7]

    When you no longer need to send messages to a topic, or the number of topics currently joined exceeds the limit, leave a topic to release resources.

    Refer to the following code to leave a topic:

    ```java
    var (status,response) = await streamChannel.LeaveTopicAsync("Motion");
    if (status.Error)
    {
        Debug.Log(string.Format("{0} failed. ErrorCode: {1}, due to {2}", status.Operation, status.ErrorCode, status.Reason));
    }
    else
    {
        Debug.Log(string.Format("User:{0} Leave Topic:{1} success! at Channel:{2}", response.UserId, response.Topic, response.ChannelName));
    }
    ```

    After a user successfully leaves a topic, the SDK triggers an `OnTopicEvent` of type `REMOTE_LEAVE`. All users in the channel, who have enabled listening to topic events, receive this event notification.

    ### Subscribe to a topic [#subscribe-to-a-topic-7]

    Joining a topic does not mean that you automatically start receiving messages sent to the topic. To receive messages, you subscribe to the topic and specify the message publishers to receive the messages from. In a single channel, you can subscribe to up to 50 topics concurrently, and in each topic, you can subscribe to up to 64 message publishers at the same time. The purpose of this limit is to balance end-side bandwidth and performance.

    To subscribe to message publishers in a topic, specify the message publishers list in the `users` parameter of the `TopicOptions` instance.

    Refer to the following code to subscribe to a topic:

    ```java
    List<string> userList = new List<string>();
    userList.Add("Tony");
    userList.Add("Marry");

    var options = new TopicOptions();
    options.users = userList.ToArray();

    var topicName = "Motion";

    var (status,response) = await streamChannel.SubscribeTopicAsync(topicName, options);
    if (result.Status.Error)
    {
        Debug.Log(string.Format("{0} failed. ErrorCode: {1}, due to {2}", status.Operation, status.ErrorCode, status.Reason));
    }
    else
    {
        Debug.Log(string.Format("The user {0} successfully subscribes topic {1} at channel {2}", response.UserId, response.Topic, response.ChannelName));
    }
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * If you do not fill in the message publishers list when subscribing to a topic, up to 64 users are randomly subscribed by default. If there are less than 64 users in the topic, all users are subscribed.

        * If you make multiple topic subscription calls, where the first subscription message publisher list is `[UserA, UserB]` and the second publisher list is `[UserB, UserC]`, then the final subscription list is `[UserA, UserB, UserC]`.
      </CalloutDescription>
    </CalloutContainer>

    ### Unsubscribe from a topic [#unsubscribe-from-a-topic-7]

    To stop receiving messages from a particular message publisher in a topic, or to unfollow the entire topic, call `UnsubscribeTopicAsync`. If you do not fill in the message publishers list when unsubscribing from a topic, you are unsubscribed from the entire topic.

    Refer to the following code to unsubscribe:

    ```java
    List<string> userList = new List<string>();
    userList.Add("Tony");
    userList.Add("Marry");

    var options = new TopicOptions();
    topicOptions.users = userList.ToArray();

    var topicName = "Motion";

    var (status,response) = await streamChannel.UnsubscribeTopicAsync(topicName, options);
    if (result.Status.Error)
    {
        Debug.Log(string.Format("{0} failed. ErrorCode:{1}, due to {2}", status.Operation, status.ErrorCode, status.Reason));
    }
    ```

    ### Add a topic event listener [#add-a-topic-event-listener-7]

    To set up an event listener for topic events, refer to [Add event listener](../send-and-receive-messages/add-event-listener). After successfully adding a topic event handler, you receive `OnTopicEvent` notifications for all topics in all stream channels you join or subscribe to. The event returns the following data:

    | Attribute     | Description                                                                                     |
    | ------------- | ----------------------------------------------------------------------------------------------- |
    | `type`        | Topic [event type](#topic-event-types)                                                          |
    | `channelName` | The name of the channel where the event occurred.                                               |
    | `publisher`   | The user ID that triggered this event.                                                          |
    | `topicInfos`  | Topic detailed information array, including topic name, topic publisher, and other information. |

    ### Topic event types [#topic-event-types-9]

    When a user in the channel joins or leaves a topic, Signaling triggers the `OnTopicEvent` event notification. Users in the channel receive the event notification in real-time and use it to track changes to the status of the topic.

    When a user joins a channel for the first time, the SDK delivers a topic event notification of type `SNAPSHOT` to the joining user. The notification contains historical status information of the topic in the current channel. The following table explains the different topic event types:

    | Attribute      | Description                                                                                                                                                                                                    |
    | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `SNAPSHOT`     | Triggered when a local user joins a stream channel for the first time. It notifies the user of details for all topics in the channel. This event notification is only triggered once when joining the channel. |
    | `REMOTE_JOIN`  | Triggered when a remote user joins a topic and registers as a publisher for the topic.                                                                                                                         |
    | `REMOTE_LEAVE` | Triggered when a remote user leaves the topic and unregisters as a topic publisher.                                                                                                                            |

    ## Reference [#reference-7]

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

    ### Topic restrictions [#topic-restrictions-7]

    A single client can have an unlimited number of topics at the same time, and there is no limit on the number of subscribers and publishers in each topic. However, to optimize the client-side bandwidth and performance, there are certain limits on the following:

    * The number of topics that a single user can join concurrently.
    * The number of topics that a user can subscribe to concurrently.
    * The number of message publishers that can be concurrently subscribed to in a topic.

    For details, see [API usage restrictions](../../reference/limitations).

    ### Topic naming [#topic-naming-7]

    A topic name is a string of up to 16 letters or numbers in the ASCII character set. All topics under the same stream channel must have unique names. The same topic name in one stream channel is considered a single topic, but the same name in different stream channels is considered as two separate topics.

    The following characters in topic names are supported:

    * 26 lowercase English letters a-z
    * 26 uppercase English letters A-Z
    * 10 numbers 0-9
    * Space
    * `!`, `#`, \`

    , `%`, `&`, `(`, `)`, `+`, `,`, `-`, `:`, `;`, `<`, `=`, `>`, `?`, `@`, `[`, `]`, `^`, `_`, `{`, `|`, `}`, `~`, `` ` ``

    <CalloutContainer type="warning">
      <CalloutTitle>
        Important
      </CalloutTitle>

      <CalloutDescription>
        Topic names cannot begin with an underscore.
      </CalloutDescription>
    </CalloutContainer>

    The following characters are not supported:

    * `.`, `*`, `/`, `\`, `\0`
    * Non-printable ASCII characters

    Although the Signaling SDK does not require it, best practice when naming a topic is to use meaningful prefix characters to indicate the purpose of the topic or the type of messages published in it. Topic naming recommendations are similar to [Channel naming recommendations](../../reference/channel-naming).

    ### API reference [#api-reference-7]

    * [`JoinTopicAsync`](/en/api-reference/api-ref/signaling#topicjoinpropsag_platform)
    * [`LeaveTopicAsync`](/en/api-reference/api-ref/signaling#topicleavepropsag_platform)
    * [`SubscribeTopicAsync`](/en/api-reference/api-ref/signaling#topicsubscribepropsag_platform)
    * [`UnsubscribeTopicAsync`](/en/api-reference/api-ref/signaling#topicunsubscribepropsag_platform)
    * [Event Listeners](/en/api-reference/api-ref/signaling#event-listeners)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>
</_PlatformTabsGroup>
