SDK : livekit/client-sdk-react-native On iOS with...
# ask-ai
m
SDK : livekit/client-sdk-react-native On iOS with Expo and development build (or even preview build), toggling the microphone off and then back on with setMicrophoneEnabled consistently triggers: • Both iOS system sound for mic toggle/untoggle • A Metro bundler error:
Copy code
Error: ENOENT: no such file or directory, open '/.../InternalBytecode.js'
    at Object.readFileSync (node:fs:448:20)
    at getCodeFrame (.../node_modules/metro/src/Server.js:997:18)
    ...
• A warning from the RN event emitter:
Copy code
WARN  An event listener wasn't added because it has been added already: bubble [Function anonymous]
That said, the mic seems to be correctly muted and unmuted. Turning off and on the camera does also trigger the Metro and Warning above. Steps to reproduce the behavior: 1. Use a minimal component (see code sample below). 2. Join a LiveKit room on iOS device. 3. Tap Mute, then tap Unmute. 4. Observe the duplicate sounds warning & Metro throwing InternalBytecode.js ENOENT.
Copy code
// Minimal component to reproduce

import {
  AudioSession,
  LiveKitRoom,
  useIOSAudioManagement,
  useLocalParticipant,
  useRoomContext,
  registerGlobals,
} from "@livekit/react-native";
import { ConnectionState } from "livekit-client";
import React, { useEffect } from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";

registerGlobals()

const SERVER_URL = "to-set";
const TOKEN = "to-set";

export default function RoomPage() {
  const handleError = React.useCallback((e: unknown) => {
    console.warn("[LiveKitRoom error]", e);
  }, []);

  // Added this
  useEffect(() => {
    let start = async () => {
      await AudioSession.startAudioSession();
    };

    start();
    return () => {
      AudioSession.stopAudioSession();
    };
  }, []);

  return (
    <LiveKitRoom
      serverUrl={SERVER_URL}
      token={TOKEN}
      connect={true}
      audio={true}
      video={false}
      onError={handleError}
    >
      <Content />
    </LiveKitRoom>
  );
}

function Content() {
  const room = useRoomContext();
  const { localParticipant } = useLocalParticipant(); // Added this

  useIOSAudioManagement(room); // Added this
  const state = room?.state ?? ConnectionState.Disconnected;

  const mute = async () => {
    try {
      await localParticipant.setMicrophoneEnabled(false);
      console.log("[Mic] setMicrophoneEnabled(false) resolved");
    } catch (e) {
      console.error("[Mic] mute failed:", e);
    }
  };

  const unmute = async () => {
    try {
      await localParticipant.setMicrophoneEnabled(true);
      console.log("[Mic] setMicrophoneEnabled(true) resolved");
    } catch (e) {
      console.error("[Mic] unmute failed:", e);
    }
  };

  return (
    <View style={s.container}>
      <Text style={s.h}>Minimal Mic Test</Text>
      <Text style={s.sub}>Connection: {state}</Text>
      <View style={s.row}>
        <Btn label="Mute" onPress={mute} />
        <Btn label="Unmute" onPress={unmute} />
      </View>
      <Text style={s.note}>
        Tip: ensure registerGlobals() is called once globally (not per refresh).
      </Text>
    </View>
  );
}

function Btn({
  label,
  onPress,
  disabled,
}: {
  label: string;
  onPress: () => void;
  disabled?: boolean;
}) {
  return (
    <Pressable
      onPress={onPress}
      disabled={disabled}
      style={[s.btn, disabled && s.btnDis]}
    >
      <Text style={s.btnTxt}>{label}</Text>
    </Pressable>
  );
}

const s = StyleSheet.create({
  container: { flex: 1, backgroundColor: "#0f1115", padding: 16 },
  h: { color: "#fff", fontSize: 18, fontWeight: "700", marginBottom: 8 },
  sub: { color: "#9fb3c8", marginBottom: 16 },
  row: { flexDirection: "row", gap: 12 },
  btn: {
    backgroundColor: "#1f6feb",
    paddingHorizontal: 14,
    paddingVertical: 10,
    borderRadius: 10,
  },
  btnDis: { opacity: 0.5 },
  btnTxt: { color: "#fff", fontWeight: "600" },
  note: { color: "#9fb3c8", marginTop: 16, fontSize: 12 },
});
Expected behavior • Mic should disable and re-enable cleanly (no double system sounds). • No Metro errors or duplicate listener warnings. Device Info: • Device: iPhone 11 • OS: 18.7 • Using Expo Dependencies Info (please reference your package-lock.json or yarn.lock file, not just your package.json): • @livekit/react-native: “^2.9.1" • livekit-client: “^2.15.7” • react-native-webrtc: “^137.0.2"
t
Thanks for reaching out, Samuel. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
I'm here to help you with the microphone toggle issues, Metro bundler errors, and duplicate event listener warnings in your iOS app using @livekit/react-native. Let's walk through some steps together: 1. Configure the iOS Audio Session: Try using
AudioSession.configureAudio()
to set the right audio category and mode. Also, make sure your
Info.plist
includes the necessary microphone and camera usage descriptions to avoid any permission hiccups. 2. Use the
useIOSAudioManagement
Hook:
This handy hook takes care of the iOS audio session lifecycle for you. Just ensure it’s integrated within your room context. 3. Resolve Metro Bundler Errors: If you’re using Yarn, running
npx yarn-deduplicate && yarn install
can help fix those dependency mismatches. If you prefer npm, you can use that as well. 4. Prevent Duplicate Event Listeners: It’s important to clean up event listeners inside the
useEffect
cleanup functions and make sure
registerGlobals()
is called just once globally to avoid duplicates. 5. Correct Usage of `setMicrophoneEnabled`: Be sure to call this method only after your room is fully connected and within the right context to ensure it works smoothly. Following these steps should help clear up the issues you’re facing. If anything feels unclear or you need more support, please don’t hesitate to reach out—I’m here to help! Sources: Camera &amp; microphone | LiveKit Docs | release/adapter_no_edge_no_global.js | src/index.tsx
a
I got it working, didn't face any issue. @miniature-accountant-10204
with expo
t
When using @livekit/react-native with Expo, I’d love to guide you through some key steps to make sure everything works smoothly: 1. Use Development Builds: Since the LiveKit SDK relies on native code, it’s important to use
expo-dev-client
and build locally. The Expo Go app unfortunately won’t support this. 2. Install Dependencies: Make sure to install all the necessary packages like
@livekit/react-native
,
@livekit/react-native-expo-plugin
, and
@livekit/react-native-webrtc
. 3. Configure Expo Plugins: Don’t forget to add the LiveKit Expo plugin and WebRTC plugin to your
app.json
or
app.config.js
. This step is crucial! 4. Initialize SDK: Early in your app’s lifecycle, call
registerGlobals()
to properly set up the WebRTC libraries. 5. Manage Audio Session: Use
AudioSession.startAudioSession()
and
stopAudioSession()
to take care of audio routing smoothly. 6. Request Permissions: Please ensure your app asks for camera and microphone permissions, and include the necessary usage descriptions in
Info.plist
if you’re on iOS. 7. Connect and Publish: Use the
LiveKitRoom
component to connect to your room and manage audio/video tracks easily with the
useLocalParticipant
hook. If you want to see everything in action, the Expo quickstart repository is a fantastic resource. And of course, I’m here if you need any more help or have questions along the way! Sources: README.md | Camera &amp; microphone | LiveKit Docs | Expo quickstart | LiveKit Docs | README.md | docs/index.html
m
@able-electrician-51473 It does work but i’m a bit concerned about this metro error and double sounds like if the mic was getting muted and unmuted when unmuting. Just want to make sure nothing is wrong in my codebase
t
It sounds like the Metro error and the double sounds during mic toggle are causing some frustration, and I’m here to help you work through them. These issues often come down to configuration or event handling, so let’s go through a few checks together to make sure your codebase is set up just right: 1. Event Listener Cleanup: Double-check that all event listeners are properly cleaned up in your
useEffect
cleanup functions. This helps avoid duplicates that might be causing trouble. 2. Audio Session Management: Make sure you’re using
AudioSession.startAudioSession()
and
stopAudioSession()
correctly to manage the audio lifecycle smoothly. 3. Single Instance of `registerGlobals()`: It’s important that
registerGlobals()
is called just once globally to prevent any duplicate WebRTC setups. 4. Debugging Metro Errors: If that Metro error keeps popping up, a good step is to run
npx yarn-deduplicate && yarn install
, or even try switching to npm to clear up any dependency mismatches. 5. Mic Toggle Behavior: The double sound might be happening because of rapid state changes. You could try adding a small delay or debounce logic to your mic toggle function to smooth things out. If you’ve gone through these and things are still tricky, please don’t hesitate to reach out. We can dive deeper into debugging together and find the right solution for you.
a
Ohh, I never got any such issue. Did you use registerGlobals? @miniature-accountant-10204 Just completed building AI voice assistant for email. Working smooth without any error.
m
Yep registerGlobals is called in my root layout. What version of the SDK and RN are you using?
t
I'm really glad to hear that
registerGlobals
is properly set up in your root layout! Regarding your question about the SDK and React Native versions, here are the versions you shared earlier: • @livekit/react-native: ^2.9.1 • livekit-client: ^2.15.7 • react-native-webrtc: ^137.0.2 If you’re wondering about compatibility or need any specific version advice, please don’t hesitate to ask—I’m here to help and guide you through it!
a
Copy code
"expo": "~53.0.22",
"react-native": "0.79.6",
"@livekit/react-native": "^2.9.1",
    "@livekit/react-native-expo-plugin": "^1.0.1",
    "@livekit/react-native-webrtc": "^137.0.1",
👍 1
@miniature-accountant-10204 ^^^
m
Just completed building AI voice assistant for email.
Did you follow any tutorial or it was your own project?
a
my own project, went through all the pages of official docs.
m
t
Thank you so much for sharing the link! It looks like this issue might be affecting more people than we thought. If the GitHub thread has any extra tips or workarounds, it could be really helpful to give those a try. Please feel free to reach out if you’d like some help understanding or applying any of the suggestions, or if you want me to assist you further in troubleshooting. I’m here to support you!