Skip to content

Bug Report: AudioManager play/pause issue on iOS #563

@alien9996

Description

@alien9996

Description

Bug Report: AudioManager play/pause issue on iOS with react-native-audio-api

Description:

There appears to be an issue with the AudioManager in the react-native-audio-api package on iOS. Specifically:

AudioManager play/pause → App UI play/pause: Works fine.
App UI play/pause → AudioManager play/pause (lock screen/control center): Does not work on iOS. The lock screen and control center UI do not respond to play/pause commands, even though the state parameter is passed as "state_playing" or "state_paused". This functionality works as expected on Android.
Environment:

React Native Version: 0.80.1
Platform: iOS (not working), Android (working)
Library: react-native-audio-api
Code Details:

Here is the relevant code used in the implementation:


// Helper function to create lock screen data
const createLockScreenData = useCallback(
(
title: string,
duration: number,
elapsedTime: number = 0,
state: MediaState = "state_paused", // Even when passing "state_playing" or "state_paused", the iOS lock screen/control center play/pause UI does not work. Android works fine.
) => {
// Validate and sanitize data for iOS
const safeDuration = Math.max(0, Math.floor(duration || 0));
const safeElapsedTime = Math.max(
0,
Math.min(Math.floor(elapsedTime || 0), safeDuration),
);

return {
  title: title || "Unknown Track",
  artist: appConfig.displayName || appConfig.name,
  album: "Meditation",
  duration: safeDuration,
  elapsedTime: safeElapsedTime,
  state,
  speed: 1.0,
};

},
[],
);

// Update lock screen info
const updateLockScreenInfo = useCallback(
(state: MediaState) => {
try {
// Only update if we have valid duration and audio data
if (!audioData?.name || playerState.duration <= 0) {
console.log("Skipping lock screen update - insufficient data");
return;
}

  const lockScreenData = createLockScreenData(
    audioData.name,
    playerState.duration,
    playerState.currentTime,
    state,
  );

  console.log("📱 Updating lock screen info:", {
    title: lockScreenData.title,
    state: lockScreenData.state,
    currentTime: lockScreenData.elapsedTime,
    duration: lockScreenData.duration,
    isPlaying: playerState.isPlaying,
  });

  AudioManager.setLockScreenInfo(lockScreenData);
} catch (error) {
  console.error("❌ Error in updateLockScreenInfo:", error);
}

},
[
audioData?.name,
playerState.duration,
playerState.currentTime,
playerState.isPlaying,
createLockScreenData,
],
);

// Setup media controls and audio session
useEffect(() => {
try {
// Configure audio session for iOS with better options
AudioManager.setAudioSessionOptions({
iosCategory: "playback",
iosMode: "default",
iosOptions: [
"allowBluetooth",
"allowAirPlay",
"defaultToSpeaker",
"allowBluetoothA2DP",
],
});

// Enable remote commands - more comprehensive for iOS
AudioManager.enableRemoteCommand("remotePlay", true);
AudioManager.enableRemoteCommand("remotePause", true);
AudioManager.enableRemoteCommand("remoteStop", true);
AudioManager.enableRemoteCommand("remoteSkipForward", true);
AudioManager.enableRemoteCommand("remoteSkipBackward", true);
AudioManager.observeAudioInterruptions(true);

// Setup remote control event listeners
remotePlaySubscriptionRef.current = AudioManager.addSystemEventListener(
  "remotePlay",
  () => {
    console.log("Remote play command received");
    // Use current refs to avoid stale closure issues
    if (!playerStateRef.current.isPlaying) {
      playAudioRef.current();
    }
  },
);

remotePauseSubscriptionRef.current = AudioManager.addSystemEventListener(
  "remotePause",
  () => {
    console.log("Remote pause command received");
    // Use current refs to avoid stale closure issues
    if (playerStateRef.current.isPlaying) {
      pauseAudioRef.current().catch(console.error);
    }
  },
);

// Handle audio interruptions (calls, other apps, etc.)
interruptionSubscriptionRef.current = AudioManager.addSystemEventListener(
  "interruption",
  (event) => {
    console.log("Audio interruption event:", event);
    if (event.type === "began") {
      // Audio was interrupted, pause if playing
      if (playerStateRef.current.isPlaying) {
        pauseAudioRef.current().catch(console.error);
      }
    } else if (event.type === "ended" && event.shouldResume) {
      // Interruption ended, resume if we should
      if (!playerStateRef.current.isPlaying) {
        playAudioRef.current().catch(console.error);
      }
    }
  },
);
console.log("Media controls setup completed");

} catch (error) {
console.error("Error setting up media controls:", error);
}

return () => {
try {
// Cleanup event listeners
remotePlaySubscriptionRef.current?.remove();
remotePauseSubscriptionRef.current?.remove();
interruptionSubscriptionRef.current?.remove();

  // Disable remote commands
  AudioManager.enableRemoteCommand("remotePlay", false);
  AudioManager.enableRemoteCommand("remotePause", false);
  AudioManager.enableRemoteCommand("remoteStop", false);
  AudioManager.enableRemoteCommand("remoteSkipForward", false);
  AudioManager.enableRemoteCommand("remoteSkipBackward", false);
  AudioManager.observeAudioInterruptions(false);

  console.log("Media controls cleanup completed");
} catch (error) {
  console.error("Error cleaning up media controls:", error);
}

};
}, []);
// ... other logic for loading and playing/pausing audio

Please investigate this issue with the AudioManager on iOS and provide guidance or a fix for enabling lock screen/control center play/pause functionality.

Steps to reproduce

1: react-native 0.80.1
2: Code function handle audio with react-native-audio-api
3: Run AudioManager and test bug on IOS

use-audio-player.txt

Snack or a link to a repository

https://github.com/software-mansion/react-native-audio-api

React Native Audio API version

0.6.4

React Native version

0.80.1

Platforms

iOS

JavaScript runtime

None

Workflow

React Native

Architecture

Fabric (New Architecture)

Build type

Debug app & dev bundle

Device

None

Device model

Iphone

Acknowledgements

Yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions