AVAudioSession.outputVolume does not reflect system volume changes made while app is in background

I have a question regarding the behavior of AVAudioSession.sharedInstance().outputVolume.

Observed behavior:

  1. When the app is in the foreground, I read audioSession.outputVolume (for example, 0.1).
  2. The app is then moved to the background.
  3. While the app is in the background, the user changes the system volume using the hardware buttons (for example, to 0.5).
  4. When the app returns to the foreground, audioSession.outputVolume still reports the previous value (0.1).

From my testing, outputVolume only seems to update when the system volume is changed while the app is in the foreground. Volume changes made while the app is in the background are not reflected when the app returns to the foreground.

Questions:

According to Apple’s documentation for AVAudioSession.outputVolume:

“The systemwide output volume set by the user.” https://developer.apple.com/documentation/avfaudio/avaudiosession/outputvolume

However, based on our testing on iOS 18.6.2 and iOS 18.1, the observed behavior seems to differ from this description. Questions:

  1. The documentation states that outputVolume represents the system-wide volume set by the user. In our testing, the value does not reflect volume changes made while the app is in the background and only updates when the app is in the foreground.Is this the expected behavior of AVAudioSession.outputVolume?
  2. Is there any other recommended way in Swift to retrieve the current system volume that reflects user changes made both while the app is in the foreground and while it is in the background?

Any clarification on the intended behavior or recommended handling would be greatly appreciated.

I had the same issue. I managed to workaround this by enabling background audio in the app's capability settings. See also: https://developer.apple.com/forums/thread/799104

I had the same problem even with "Audio, AirPlay and Picture in Picture" background mode enabled and with all the AVAudioSession.setActive(true) and AVAudioSession.setCategory(...) calls.

We run some tests and it seems Apple made things more restrictive since iOS 18 (it was working on iOS 17.6). The only fix I found is to use the good old MPVolumeView as a substitute, but there's a catch - it's mandatory to add it to UI otherwise it will always return volume of 0.

In SwiftUI it was as simple as creating a UI component:

import MediaPlayer
import SwiftUI

struct VolumeSlider: UIViewRepresentable {

    private let volumeView: MPVolumeView = MPVolumeView()

    func makeUIView(context: Context) -> MPVolumeView {
        return volumeView
    }

    func updateUIView(_ view: MPVolumeView, context: Context) {}

    func getVolume() -> Float {
        if let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider {
            return slider.value
        }
        return AVAudioSession.sharedInstance().outputVolume
    }
}

And then in your SwiftUI view you can add it like:

struct YourView: View {
    private let volumeControl: VolumeSlider = VolumeSlider()

    @ViewBuilder
    var body: some View {
        //... your controls here ...

        // This is a hack to make volume control work, but be invisible
        volumeControl.frame(height: .zero).clipped()

        Spacer(minLength: .zero)
    }

    private func someMethodThatRelatesToVolumeControl() {
        let volume = volumeControl.getVolume()
        // use the actual volume value
    }
}
AVAudioSession.outputVolume does not reflect system volume changes made while app is in background
 
 
Q