3

I am trying to show a volume indicator in my app, but first I need to monitor the systems current volume.

I am using an observer, and while the print statement shows the correct value, the UI never does.

import SwiftUI
import MediaPlayer

struct ContentView: View {
    @State var vol: Float = 1.0

    // Audio session object
    private let session = AVAudioSession.sharedInstance()
    // Observer
    private var progressObserver: NSKeyValueObservation!

    init() {
            do {
                try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient)
                try session.setActive(true, options: .notifyOthersOnDeactivation)
                self.vol = 1.0
            } catch {
                print("cannot activate session")
            }

            progressObserver = session.observe(\.outputVolume) { [self] (session, value) in
                print(session.outputVolume)
                self.vol = session.outputVolume
            }
        }

    var body: some View {
        Text(String(self.vol))
    }
}

// fixed (set category to ambient)(updated above code) Also, every time the application is launched, it stops all currently playing music.

Marcelo
  • 157
  • 9
  • 1
    You are claiming the active audio session, so it will stop any other audio sessions. This is because the default category is solo ambient. You need to set the audio session category to ambient. – Paulw11 May 06 '20 at 22:16
  • @Paulw11 Thanks for the suggestion to set the category to ambient. That solves that issue. – Marcelo May 06 '20 at 22:22

1 Answers1

3

Solved. Created a class that conforms to ObservableObject and use the ObservedObject property in the view. Also, the volume observer doesn't work in the simulator, only on device.

VolumeObserver.swift

import Foundation
import MediaPlayer

final class VolumeObserver: ObservableObject {
    
    @Published var volume: Float = AVAudioSession.sharedInstance().outputVolume
    
    // Audio session object
    private let session = AVAudioSession.sharedInstance()
    
    // Observer
    private var progressObserver: NSKeyValueObservation!
    
    func subscribe() {
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient)
            try session.setActive(true, options: .notifyOthersOnDeactivation)
        } catch {
            print("cannot activate session")
        }
        
        progressObserver = session.observe(\.outputVolume) { [self] (session, value) in
            DispatchQueue.main.async {
                self.volume = session.outputVolume
            }
        }
    }
    
    func unsubscribe() {
        self.progressObserver.invalidate()
    }
    
    init() {
        subscribe()
    }
}

ContentView.swift

import SwiftUI
import MediaPlayer

struct ContentView: View {

    @ObservedObject private var volObserver = VolumeObserver()
    
    init() {
        print(volObserver.volume)
    }
    
    var body: some View {
        Text(String(volObserver.volume))
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Ryan Sam
  • 2,858
  • 4
  • 19
  • 30
Marcelo
  • 157
  • 9
  • Is it possible to get the observer to respond even if the volume is at 0.0 or 1.0? Seems like if the volume is set at the max/min, the observer does not respond... – Benjamin B. Jun 26 '22 at 22:07
  • I must be missing something because the answer given does not contain a value `val` – Kurt L. Nov 12 '22 at 03:53