// see if you currently have this permission.
let hasPermission = call.currentUserHasCapability(.sendAudio)
// request the host to grant you this permission.
let response = try await call.request(permissions: [.sendAudio])Permissions & Moderation
In some types of calls, there's a requirement to moderate the behaviour of the participants. Examples include muting a participant, or ending the call for everyone. Those capabilities are usually reserved for the hosts of the call (users with elevated capabilities). They usually have additional moderation controls in their UI, that allow them to achieve these actions.
The StreamVideo SDK has support for such capabilities, with the usage of the Call's permissions features.
Requesting & Granting permission
This example shows how to check if you have permissions to do something and ask for permission. Let's say that you've joined an audio room and want to speak
Permission requests are exposed from call.state on the permissionRequests published variable.
if let request = call.state.permissionRequests.first {
// reject it
request.reject()
// grant it
try await call.grant(request: request)
}You can also grant permissions directly using call.grantPermissions() method like the example below:
try await call.grant(permissions: [.sendAudio], for: "thrierry")You can request the following 3 permissions: send-audio, send-video, and screenshare.
Revoking permissions
Similarly to granting permissions, the host, can also revoke permissions for any user in the call.
// revoke user's with id "tommaso", the permission to send audio
let response = try await call.revoke(permissions: [.sendAudio], for: "tommaso")Moderation Capabilities
// block a user
try await call.blockUser(with: "tommaso")
// unblock a user
try await call.unblockUser(with: "tommaso")
// remove a member from a call
try await call.removeMembers(ids: ["tommaso"])Alternatively you can also mute users.
// mutes all users (audio and video are true by default) other than yourself
try await call.muteAllUsers()
// mute user with id "tommaso" specifically
try await call.mute(userId: "tommaso")
// mute only audio for a specific user
try await call.mute(userId: "tommaso", audio: true, video: false)
// mute only video for a specific user
try await call.mute(userId: "tommaso", audio: false, video: true)Listening to Permission Events
You can listen for permission-related events to update your UI:
for await event in call.subscribe() {
switch event {
case .typePermissionRequestEvent(let requestEvent):
// A user is requesting permission
print("User \(requestEvent.user.id) requested permissions")
case .typeUpdatedCallPermissionsEvent(let permissionsEvent):
// Permissions were updated for a user
print("Permissions updated for user: \(permissionsEvent.user.id)")
default:
break
}
}Building a Moderation UI
Here's an example of a participant moderation menu:
struct ParticipantModerationMenu: View {
let call: Call
let participant: CallParticipant
@State private var showAlert = false
@State private var alertMessage = ""
var body: some View {
Menu {
Button(role: .destructive) {
muteParticipant()
} label: {
Label("Mute", systemImage: "mic.slash")
}
Button(role: .destructive) {
muteParticipantVideo()
} label: {
Label("Turn off video", systemImage: "video.slash")
}
Button(role: .destructive) {
blockParticipant()
} label: {
Label("Block", systemImage: "hand.raised")
}
Button(role: .destructive) {
removeParticipant()
} label: {
Label("Remove from call", systemImage: "person.badge.minus")
}
} label: {
Image(systemName: "ellipsis")
.padding()
}
.alert("Moderation", isPresented: $showAlert) {
Button("OK") { }
} message: {
Text(alertMessage)
}
}
private func muteParticipant() {
Task {
do {
try await call.mute(userId: participant.userId, audio: true, video: false)
} catch {
alertMessage = "Failed to mute: \(error.localizedDescription)"
showAlert = true
}
}
}
private func muteParticipantVideo() {
Task {
do {
try await call.mute(userId: participant.userId, audio: false, video: true)
} catch {
alertMessage = "Failed to disable video: \(error.localizedDescription)"
showAlert = true
}
}
}
private func blockParticipant() {
Task {
do {
try await call.blockUser(with: participant.userId)
} catch {
alertMessage = "Failed to block: \(error.localizedDescription)"
showAlert = true
}
}
}
private func removeParticipant() {
Task {
do {
try await call.removeMembers(ids: [participant.userId])
} catch {
alertMessage = "Failed to remove: \(error.localizedDescription)"
showAlert = true
}
}
}
}Checking Capabilities
Before showing moderation controls, check if the current user has the required capabilities:
let canMuteOthers = call.currentUserHasCapability(.muteUsers)
let canBlockUsers = call.currentUserHasCapability(.blockUsers)
let canRemoveMembers = call.currentUserHasCapability(.removeCallMember)
let canEndCall = call.currentUserHasCapability(.endCall)
// Only show moderation UI if the user has capabilities
if canMuteOthers || canBlockUsers || canRemoveMembers {
ParticipantModerationMenu(call: call, participant: participant)
}