Marcelo
Marcelo

Reputation: 157

Observing system volume in SwiftUI

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.

Upvotes: 3

Views: 2647

Answers (1)

Marcelo
Marcelo

Reputation: 157

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 {

    @StateObject 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()
    }
}

Upvotes: 3

Related Questions