Things take time

[SWIFT] 오디오 감지를 위한 옵저버 AVSystemController_SystemVolumeDidChangeNotification 사용 불가(iOS15) 본문

iOS (기능)

[SWIFT] 오디오 감지를 위한 옵저버 AVSystemController_SystemVolumeDidChangeNotification 사용 불가(iOS15)

겸손할 겸 2021. 7. 5. 14:36

[용도]

 

사용자가 직접 하드웨어 볼륨키를 이용하여 사운드를 변경했을 때를 감지하기 위한 방법으로 아래와 같은 코드를 사용했다.

NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged), name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil)

그런데 iOS15 베타버전을 설치하여 이것저것 테스트 해보는 도중, 위의 코드가 되지 않는 문제가 발생했다. 15미만은 정상 작동함.

 

기본적으로 위에서 사용하는 코드는, AVSystemController_SystemVolumeDidChangeNotification 이란 문자열 값을 사용하여, 직접 사용하고 있는 것인데 이 값은 iOS 버전이 올라가면 값이 바뀔 수 있다. 이유는 애플에서 직접 제공하는 Property값이 아닌 값이기에 지원하지 않아도 이상하지 않은 것이다.

 

그러므로 직접 애플에서 고시한 property를 사용하는 것이 올바른 방법이겠다.

 

 

[코드]

AVFoundation을 import하고,

        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setActive(true)
        } catch {
            print("[Gyeom] Error occured :: \(error)")
        }
        audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)

위와 같은 오디오 세션에 직접 옵저버를 할당한다. forKeyPath에 들어가는 outputVolume값은 아래 링크에서 확인할 수 있다.

 

https://developer.apple.com/documentation/avfaudio/avaudiosession/1616533-outputvolume

 

Apple Developer Documentation

 

developer.apple.com

문서를 읽다보면, key-value observing을 통해 해당 값에 대한 감지가 가능하다고 하는데 이를 줄여 KVO방식이라 한다.

 

많이 들어봤을 수 있겠지만, RxSwift와 같은 종류의 개발 문법(?)라이브러리가 이런 방식으로 코딩하는 것으로 알고 있다.(Rx공부해야하는데.. SwiftUI + Combine도..)

 

특정 변수나, 값에 대한 변화를 감지하여 해당 값 변화가 이루어 졌을 때 UI를 업데이트 한다던지에 대한 코드를 작성해서 재사용성을 줄이는 것으로 알고있는데 뭐 그런 방식으로 해당 프러퍼티의 값을 미리 하는 것이다.

 

그리고 아래의 함수를 오버라이드한다.

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print("[Gyeom]observeValue call")
        let audioSession = AVAudioSession.sharedInstance()
        if keyPath == "outputVolume"{
            let volume = audioSession.outputVolume
            print("[Gyeom] volume : \(audioSession.outputVolume)")
        }
    }

옵저버로 등록한 것이 있다면, 위의 함수가 호출되는데 이 때 keyPath가 위에 등록한 outputVolume이라면, 기존의 작업을 수행하면 되는 것이다. 사용자가 설정한 값은 AudioSession.sharedInstance().outputVolume으로 얻어 낼 수 있다.

 

 

[출처]

https://stackoverflow.com/questions/68249775/in-ios15-system-volume-change-observer-not-working

 

in ios15, System Volume Change Observer not working

I used following code for detecting system volume changed by users. NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged), name: NSNotification.Name(rawValue: "

stackoverflow.com

내가 올림 ㅋ

 

 

추가 => 2021/10/22

 

위의 내용대로 화면은 정상적으로 구현되나, 화면 뷰 컨트롤러가 여러개인 경우 해당 옵저버가 등록된후 제거해주지 않으면 안된다. 

AudioSession.sharedInstance().removeObserver를 등록해주면되고, 현재 나는 아래의 함수로 변경해서 사용중이다.

 

        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setActive(true)
        } catch {
            print("[Gyeom] Error occured :: \(error)")
        }
        // NSKeyValueObservation
        self.volumeChangeObservation = audioSession.observe(\.outputVolume, changeHandler: {
            (av, _) -> Void in
            let volume = av.outputVolume
            if volume > 0 {
            
            }else {
            
            }
        })
        // audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)

그리고 화면이 종료되거나 메모리 할당 될 때

self.volumeChangeObservation = nil