Skip to main content

Speaking While Muted

Some calling apps show an indicator when you are trying to speak while you are muted. This is helpful for users that forgot to turn on the microphone when they try to speak.

You can easily add such functionality with the StreamVideo iOS SDK. One approach to achieve this is to add an overlay to the CallView with the indicator. The logic for displaying the popup can be determined by listening to sounds even when the microphone is turned off. When a sound goes over a certain threshold, we will display the indicator.

Here's an example implementation for this approach, using the MicrophoneChecker from the SDK:

struct CustomCallView<Factory: ViewFactory>: View {

@Injected(\.colors) var colors

var viewFactory: Factory
@ObservedObject var viewModel: CallViewModel

@StateObject var microphoneChecker = MicrophoneChecker()
@State var mutedIndicatorShown = false

var body: some View {
CallView(viewFactory: viewFactory, viewModel: viewModel)
.onReceive(viewModel.$callSettings) { callSettings in
.onReceive(microphoneChecker.$$audioLevels, perform: { values in
guard !viewModel.callSettings.audioOn else { return }
for value in values {
if (value > -50 && value < 0) && !mutedIndicatorShown {
mutedIndicatorShown = true
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: {
mutedIndicatorShown = false
mutedIndicatorShown ?
VStack {
Text("You are muted.")
: nil
.onDisappear {
.onAppear {

private func updateMicrophoneChecker() {
if !viewModel.callSettings.audioOn {
} else {

In the implementation, we are listening to the callSettings changes from the view model. Based on that, we decide whether we should listen for sounds from the microphone checker.

Additionally, we are listening to the @Published property called decibels from the MicrophoneChecker, which returns an array of the last 3 decibels. If a value passes our threshold, then we set the mutedIndicatorShown to true, which displays a simple text popup. We reset the value to false after 2 seconds, to hide the popup.

Finally, we need to use the custom call view in the custom ViewFactory:

func makeCallView(viewModel: CallViewModel) -> some View {
CustomCallView(viewFactory: self, viewModel: viewModel)

Did you find this page helpful?