I’m developing a React component for audio playback in a chat application. The component works well on Android devices, but I’ve encountered an issue on iOS devices. When connecting Bluetooth headsets or AirPods, the audio still plays through the device’s speaker instead of the Bluetooth device.
I’ve tried to use the selectAudioOutput method to allow users to select an audio output device, but this method seems to be experimental and not widely supported. Additionally, there are TypeScript errors and compatibility issues.
Here’s the relevant part of my code:
import { useEffect, useState } from "react";
// Extend the HTMLAudioElement type to include setSinkId
interface ExtendedHTMLAudioElement extends HTMLAudioElement {
setSinkId(sinkId: string): Promise<void>;
}
// Extend the MediaDevices type to include selectAudioOutput
interface ExtendedMediaDevices extends MediaDevices {
selectAudioOutput?: () => Promise<MediaDeviceInfo>;
}
const AudioChat = () => {
const [audioDevices, setAudioDevices] = useState<MediaDeviceInfo[]>([]);
const [selectedAudioDevice, setSelectedAudioDevice] = useState<string>("");
const selectAudioOutputDevice = async () => {
const extendedMediaDevices = navigator.mediaDevices as ExtendedMediaDevices;
if (extendedMediaDevices.selectAudioOutput) {
try {
const audioDevice = await extendedMediaDevices.selectAudioOutput();
setSelectedAudioDevice(audioDevice.deviceId);
} catch (error) {
console.error("Error selecting audio output device:", error);
}
} else {
console.log("selectAudioOutput() not supported or not in secure context.");
}
};
const getAudioDevices = async () => {
const devices = await navigator.mediaDevices.enumerateDevices();
const audioOutputDevices = devices.filter(device => device.kind === "audiooutput");
setAudioDevices(audioOutputDevices);
};
const selectAudioDevice = async () => {
if (selectedAudioDevice && 'setSinkId' in HTMLMediaElement.prototype) {
try {
const audioElement = new Audio() as ExtendedHTMLAudioElement;
await audioElement.setSinkId(selectedAudioDevice);
} catch (error) {
console.error("Error setting audio output device:", error);
}
}
};
useEffect(() => {
getAudioDevices();
}, []);
return (
<div className="audioDeviceSelector">
<label htmlFor="audioDevices">Select Audio Device:</label>
<select
id="audioDevices"
value={selectedAudioDevice}
onChange={(e) => setSelectedAudioDevice(e.target.value)}
>
<option value="">Default</option>
{audioDevices.map((device) => (
<option key={device.deviceId} value={device.deviceId}>
{device.label}
</option>
))}
</select>
<button onClick={selectAudioOutputDevice}>Select Audio Output</button>
</div>
);
};
export default AudioChat;
The audio starts playing through the connected bluetooth device if I disconnect and reconnect it. But not all users will understand this. So this needs to be fixed.
Is there a workaround or a more widely supported method to allow users to select an audio output device on iOS?
Any suggestions or solutions for these issues would be greatly appreciated!
I tried setSinkId method, I tried selectAudioOutput method, but non of them actually worked. I am expecting that the audio should be played through connected Bluetooth device and not form the iPhones speaker.
1