Skip to main content
Version: 4.x.x

PubSub - iOS

PubSub is a short acronym for Publish-Subscribe mechanism. This mechanism is used to send and receive messages from a particular topic. As the name suggests, for someone to send a message, they have to specify the topic and the message which should be published and for someone to receive a message, they should be subscribed to that topic.

Here is a visual to better understand publish-subscribe mechanism.

pubsub

PubSub​

In order to use PubSub in a room, VideoSDK provides the PubSub class, available as room.pubSub, which allows you to subscribe to any topic and publish to any topic allowing to pass on messages and instructions in the room easily.

publish()​

  • This method is used for publishing message of specific topic.
  • This method can be accessed from room.pubSub. It is async throws, so call it with try await.
  • This method will accept following parameters as input:
    • topic: This will be the topic for which you are publishing a message.
    • message: This will be the actual message to be published. It has to be in String format.
    • options: This is a PubSubPublishOptions value with 2 properties. It is optional.
      • persist : persist keeps the message for the duration of the session. When persist is set to true, that message will be retained for upcoming participants and will be available in VideoSDK Session Dashboard with .CSV format after completion of session. Default: false.
      • sendOnly: If you want to send a message to specific participants, you can pass their respective participantId as a [String]. If you leave it nil, the default, the message will be sent to all participants.
    • payload: Optional [String: Any] data sent along with the message. Subscribers receive it in the message's payload.
//Button that will send the message when tapped
@IBAction func sendMessageTapped(_ sender: Any) {
let options = PubSubPublishOptions(persist: true)
// publish message
Task {
do {
try await self.room?.pubSub.publish(topic: "CHAT", message: "How are you?", options: options)
} catch {
print("error while publish: \(error)")
}
}
}

subscribe()​

  • This method is used to subscribe for particular topic.

  • room.pubSub.subscribe(topic:listener:options:) accepts a listener and optional PubSubSubscribeOptions. It is async throws. All messages — realtime and old — are delivered exclusively through the listener callbacks; nothing is returned from the awaited call.

  • This method will accept following parameters as input:

    • topic: This will be the topic to be subscribed.

    • listener: This is an object of PubSubMessageListener containing listener callbacks:

      • onMessageReceived(_:) — Called for every realtime message as soon as it is received.

        • Use this when: You want to handle each message individually, such as appending it to a chat, triggering a notification, or updating message-specific state.
        • Don't use this when: The topic is high-throughput / large-scale (busy chats, live reactions, cursor positions). Firing once per message causes excessive re-renders and state updates — use onBatchReceived instead.
      • onBatchReceived(_:) — Called with an array of realtime messages. These are the same messages delivered through onMessageReceived, but grouped into a single batch.

        • Use this when: Processing messages in bulk is more efficient, such as performing a single state update or rendering a large list of messages.
      • onOldMessagesReceived(_:info:) — Called with persisted history in batches — fires once per batch until history is fully delivered. Only messages published with persist: true are delivered here. info.isLast is true on the final batch. If the topic has no old messages, it is not called.

        • Use this when: Loading chat history or restoring previously sent messages when a participant joins the room.
      • onMessageDrop(_:) — Called when incoming realtime messages cannot be delivered. This can happen:

        • because the client cannot keep up with the incoming message rate (for example, due to a slow network or high CPU usage), causing overflow, or
        • because newMessageLimit is configured, which intentionally limits how many realtime messages are delivered every 500 ms. Any messages exceeding the configured limit within that window are discarded.

        info.droppedCount contains the number of messages that were not delivered.

        • Use this when: Notifying users that some realtime messages were missed, monitoring the effects of newMessageLimit, or logging dropped messages for debugging and observability.
    • options: This is an object of PubSubSubscribeOptions. Build it with PubSubSubscribeOptions(oldMessageLimit:realtimeOverflow:maxQueue:newMessageLimit:), or create PubSubSubscribeOptions() and set its properties:

      • oldMessageLimit (Int?) — how many old messages to receive. Pass 0 to receive none. If you leave it nil, all old messages are delivered. Default: all.

        • When to use: Cap the history for large sessions where loading every persisted message would be wasteful (e.g. only the last 50 messages need to appear when a participant joins).
      • realtimeOverflow (RealtimeOverflow Enum) — behavior when your internet is slow or your device can't keep up. RealtimeOverflow.QUEUE (default) queues remaining messages so you receive them once you catch up; RealtimeOverflow.DROP drops them instead.

        • When to use: Use RealtimeOverflow.QUEUE for chat where every message matters. Use RealtimeOverflow.DROP for high-frequency, low-value streams (live reactions, cursor positions) where stale data is worse than missing data.
      • maxQueue (Int?) — maximum number of message batches to queue during overflow. Leave it nil (the default) to let the server decide; the server default is 70 and the maximum is 200. Only valid with realtimeOverflow: .QUEUE.

        • When to use: Increase this value when you want to retain more messages and catch up after recovering from a slow network or CPU spike. Lower it if memory pressure matters more than history.
      • newMessageLimit (Int) — maximum number of realtime messages to receive per 500 ms.

        • When to use: Set this when you want to render only a particular number of messages per second — for example, throttle a busy chat topic so the UI doesn't jank, or rate-limit a reactions topic.
note

If you call subscribe() again on the same topic with a different listener, the values of realtimeOverflow, maxQueue, and newMessageLimit are overridden by the latest subscribe() call. Other options (such as oldMessageLimit) are not affected.

Every message delivered to the listener contains the following fields:

  • id — unique identifier for the message.
  • message — the actual message content that was sent.
  • senderId — participantId of the participant who sent the message.
  • senderName — displayName of the participant who sent the message.
  • timestamp — the timestamp indicating when the message was published.
  • topic — the topic the message was published to.
  • payload — any additional data sent along with the message (optional).
  • sendOnly — the participant IDs the message was sent to. It is empty when the message was sent to everyone.
extension RoomViewController: RoomEventListener {

/// Room joined
func onRoomJoined() {
Task {
var demoOptions = PubSubSubscribeOptions()
demoOptions.oldMessageLimit = 50
demoOptions.realtimeOverflow = .QUEUE
demoOptions.maxQueue = 70
demoOptions.newMessageLimit = 20

do {
try await room?.pubSub.subscribe(
topic: "CHAT",
listener: self,
options: demoOptions
)
} catch {
print("CHAT subscribe failed: \(error)")
}
}
}
}

extension RoomViewController: PubSubMessageListener {
// read message when it is received
func onMessageReceived(_ message: PubSubMessage) {
print("Message Received: " + message.message)
}

func onBatchReceived(_ messages: [VideoSDKRTC.PubSubMessage]) {
// Handle a batch of realtime messages together
print("Batch messages: \(messages.count)")
}

func onOldMessagesReceived(_ messages: [VideoSDKRTC.PubSubMessage], info: PubSubHistoryInfo) {
print("onOldMessagesReceived: \(messages.count) | isLast: \(info.isLast)")
}

func onMessageDrop(_ info: PubSubMessageDropInfo) {
// Called when incoming messages are dropped
print("\(info.droppedCount) message(s) dropped")
}
}

unsubscribe()​

  • This method is used to unsubscribe for particular topic.

  • This method will accept two parameters as input:

    • topic: This will be the topic to be unsubscribed.
    • listener: This is an object of PubSubMessageListener, which was passed in subscribe().
  • Call it before you leave the room. After you leave, it can fail with ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED.

func unsubscribe() {
//unsubscribe from the topic 'CHAT' when you no longer need it
Task {
do {
try await self.room?.pubSub.unsubscribe(topic: "CHAT", listener: self)
} catch {
print("unsubscribe failed: \(error)")
}
}
}

Errors​

All PubSub methods are async throws. Every failure is thrown as a VideoSDKError, so catch it with catch let error as VideoSDKError and read its code, name and message.

publish() throws:

  • ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED — the operation was attempted before the room was joined (or after it left).
  • ERROR_ROOM_RECONNECTING — the room is reconnecting. Retry once it reconnects.
  • PUBSUB_PUBLISH_FAILED — the publish failed, for example because it timed out. The message holds the reason.

subscribe() throws:

  • ERROR_INVALID_PARAMETER — the options are invalid, or this listener is already subscribed to the topic.
    • The options are invalid when oldMessageLimit or newMessageLimit is negative, maxQueue is less than 1, or maxQueue is set while realtimeOverflow is .DROP.
    • A listener can subscribe to a topic only once. unsubscribe() first if you need to re-subscribe. If you need to listen to the messages in different places, create another listener and call subscribe() with it.
  • ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED — the operation was attempted before the room was joined (or after it left).
  • ERROR_ROOM_RECONNECTING — the room is reconnecting. Retry once it reconnects.
  • PUBSUB_SUBSCRIBE_FAILED — the subscribe failed. The message holds the reason.

unsubscribe() throws:

  • ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED, ERROR_ROOM_RECONNECTING or PUBSUB_UNSUBSCRIBE_FAILED — the topic could not be unsubscribed on the server. This can happen only when you remove the topic's last listener. The listener stops receiving messages either way. Unsubscribing a listener that is not subscribed does nothing.

ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED and the PUBSUB_*_FAILED errors are also sent to onError(error:), so don't show them to the user twice.

Applications of PubSub​

Common uses of PubSub in a room are listed below:

  1. Chat: You can utilise this to develop various Chat features, such as Private Chat and Group Chat. You can follow our chat integration guide here.
  2. Raise Hand: You can allow attendees to raise their hands at any time in the room, informing everyone else that someone has done so.
  3. Poll: You may make polls, let users respond to them, and display the results at the end of a poll.
  4. Question Answer Session: You can also design interactive functionality that is question-and-answer based.

For live data that matters only at the moment it is sent, such as cursor positions, use the Data Channel instead. Its messages have no topic and are not stored on the server.

Downloading PubSub Messages​

All PubSub messages published with persist: true can be downloaded as a .csv file. This file is available in the VideoSDK dashboard and through the Sessions API.

API Reference​

The API references for all the methods and events utilised in this guide are provided below.

Got a Question? Ask us on discord