Skip to main content
Version: 4.x.x

Change Mode - iOS

In a live stream, audience members usually join in RECV_ONLY mode, meaning they can only view and listen to the hosts. However, if a host invites an audience member to actively participate (e.g., to speak or present), the audience member can switch their mode to SEND_AND_RECV using the changeMode() method.

This guide explains how to use the changeMode() method and walks through a sample implementation where a host invites an audience member to become a host using PubSub.

caution

changeMode() only ever changes the mode of the participant who calls it. There is no form of changeMode() that takes another participant's id, and a host cannot change an audience member's mode directly.

Promotion therefore takes two pieces of code, one on each side, and neither does anything on its own:

  1. On the host — publish a request to the invited participant over PubSub. Step 1 and Step 2 below.
  2. On the invited participant — subscribe to that same topic and, in the message handler, call changeMode() yourself. Step 3 below. Without this subscriber the request reaches nobody and no mode ever changes.

An implementation that only publishes the request looks finished and does nothing.

changeMode()​

  • The changeMode() method from the Room class switches the calling participant's own mode during a live stream — for example, an audience member becoming a host.
  • It takes only the target mode. To move a different participant between modes, send them a PubSub request and let their client call changeMode() itself, as shown in the Implementation Guide below.
  • It is async throws, so call it with try await inside a Task. It throws a VideoSDKError if the change fails, for example ERROR_ALREADY_IN_REQUESTED_STATE when the participant is already in that mode.
  • Switching to RECV_ONLY or SIGNALLING_ONLY stops the participant's screen share and turns off their camera and microphone.
  • After a switch to SEND_AND_RECV, the SDK turns the camera and microphone back on if they were on before the participant left SEND_AND_RECV. For a participant who joined in RECV_ONLY, it turns on the devices set with micEnabled and camEnabled in createRoom(). Call unmuteMic() or enableCam() only for a device that stays off, for example when camEnabled was false. For a device that is already on, these calls throw ERROR_ALREADY_IN_REQUESTED_STATE.

Example: a participant changing its own mode​

@IBAction func changeModeTapped(_ sender: UIButton) {
Task {
do {
try await room?.changeMode(.SEND_AND_RECV)
print("Mode changed to SEND_AND_RECV")
} catch {
print("Failed to change mode: \(error)")
}
}
}

Implementation Guide​

Step 1 : Create a Pubsub Topic​

  • Set up a PubSub topic to send a mode change request from the host to a specific audience member.
func inviteParticipantAsHost(participantId: String) {
let topic = "REQUEST_TO_JOIN_AS_HOST_\(participantId)"
let message = "SEND_AND_RECV"
Task {
do {
try await room?.pubSub.publish(topic: topic, message: message)
print("Invitation sent to participant: \(participantId)")
} catch {
print("Failed to send the invitation: \(error)")
}
}
}

Step 2 : Create an Invite Button​

  • Add an "Invite on Stage" button for each audience member. When clicked, it publishes a PubSub message with the mode "SEND_AND_RECV" to that participant.
func inviteButtonTapped(for participant: Participant) {
// Only invite participants who are in RECV_ONLY mode
guard participant.mode == .RECV_ONLY else { return }
inviteParticipantAsHost(participantId: participant.id)
}

Step 3 : Create a Listener to Change the Mode​

  • On the audience side, subscribe to the specific PubSub topic. When a mode request is received, update the participant’s mode using changeMode().
  • Call setupModeChangeListener() after the live stream is joined, for example in onRoomJoined(). Before that, subscribe() throws.
func setupModeChangeListener() {
guard let localParticipantId = room?.localParticipant.id else { return }

// Subscribe to the specific topic for this participant
let topic = "REQUEST_TO_JOIN_AS_HOST_\(localParticipantId)"

Task {
do {
try await room?.pubSub.subscribe(topic: topic, listener: self)
} catch {
print("Failed to subscribe: \(error)")
}
}
}

extension LiveStreamViewController: PubSubMessageListener {
func onMessageReceived(_ message: PubSubMessage) {
guard message.topic.hasPrefix("REQUEST_TO_JOIN_AS_HOST_"),
let mode = Mode(rawValue: message.message) else { return }

// When a request is received, change the mode
Task {
do {
try await room?.changeMode(mode)
print("Mode changed to \(mode.rawValue)")
} catch {
print("Failed to change mode: \(error)")
}
}
}
}
note

Calling changeMode() changes only the caller's mode. Every other client must rebuild its host and audience lists from the onParticipantModeChanged event, as described in Updating the UI on mode change.

Updating the UI on mode change​

changeMode() updates the mode of the participant who calls it. The room then sends an onParticipantModeChanged event to every participant, including clients that did not initiate the change. Each client should use that event to update its UI.

In the event handler, rebuild the lists that drive your UI by filtering participants by their current mode, then refresh the view. Simply pinning, unpinning, or displaying a message is not enough: when a participant is promoted, their mode changes, but other clients will not automatically move them from the audience to the stage.

func onParticipantModeChanged(participantId: String, mode: VideoSDKRTC.Mode) {
// Re-derive both lists from the current participants, then reload.
speakers = participantsWithMode(.SEND_AND_RECV)
viewers = participantsWithMode(.RECV_ONLY)
collectionView.reloadData()
}

// The local participant and every remote participant that is in the given mode.
func participantsWithMode(_ mode: Mode) -> [Participant] {
guard let room = room else { return [] }
let everyone = [room.localParticipant] + Array(room.participants.values)
return everyone.filter { $0.mode == mode }
}
caution

Rebuild both lists — the speakers and the viewers. Removing the participant from the audience list alone is the common half-fix: they disappear from the audience and never appear on stage.

API Reference​

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

Got a Question? Ask us on discord