rockhammer rockhammer - 3 months ago 79
Swift Question

How to resume audio after interruption in Swift?

I am following instructions here, I've put together this test project to handle interruptions to audio play. Specifically, I'm using the alarm from the default iphone clock app as interruption. It appears that the interruption handler is getting called but is not getting past the

let = interruptionType
line as "wrong type" showed up twice.

import UIKit
import AVFoundation

class ViewController: UIViewController {

var player = AVAudioPlayer()

let audioPath = NSBundle.mainBundle().pathForResource("rachmaninov-romance-sixhands-alianello", ofType: "mp3")!

func handleInterruption(notification: NSNotification) {

guard let interruptionType = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? AVAudioSessionInterruptionType else { print("wrong type"); return }

switch interruptionType {

case .Began:
print("began")
// player is paused and session is inactive. need to update UI)
player.pause()
print("audio paused")

default:
print("ended")
/**/
if let option = notification.userInfo?[AVAudioSessionInterruptionOptionKey] as? AVAudioSessionInterruptionOptions where option == .ShouldResume {
// ok to resume playing, re activate session and resume playing
// need to update UI
player.play()
print("audio resumed")
}
/**/
}
}

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.

do {
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
player.numberOfLoops = -1 // play indefinitely
player.prepareToPlay()
//player.delegate = player

} catch {
// process error here
}

// enable play in background http://stackoverflow.com/a/30280699/1827488 but this audio still gets interrupted by alerts
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
} catch let error as NSError {
print(error.localizedDescription)
}
} catch let error as NSError {
print(error.localizedDescription)
}

// add observer to handle audio interruptions
// using 'object: nil' does not have a noticeable effect
let theSession = AVAudioSession.sharedInstance()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.handleInterruption(_:)), name: AVAudioSessionInterruptionNotification, object: theSession)

// start playing audio
player.play()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}


Furthermore, following an idea here, I have modified the handler to

func handleInterruption(notification: NSNotification) {

//guard let interruptionType = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? AVAudioSessionInterruptionType else { print("wrong type"); return }

if notification.name != AVAudioSessionInterruptionNotification
|| notification.userInfo == nil{
return
}

var info = notification.userInfo!
var intValue: UInt = 0
(info[AVAudioSessionInterruptionTypeKey] as! NSValue).getValue(&intValue)
if let interruptionType = AVAudioSessionInterruptionType(rawValue: intValue) {

switch interruptionType {

case .Began:
print("began")
// player is paused and session is inactive. need to update UI)
player.pause()
print("audio paused")

default:
print("ended")
/** /
if let option = notification.userInfo?[AVAudioSessionInterruptionOptionKey] as? AVAudioSessionInterruptionOptions where option == .ShouldResume {
// ok to resume playing, re activate session and resume playing
// need to update UI
player.play()
print("audio resumed")
}
/ **/
player.play()
print("audio resumed")
}
}
}


Results are that all of "began", "audio paused", "ended" and "audio resumed" show up in console but audio play is not actually resumed.

Note: I moved the
player.play()
outside of the commented out
where option == .ShouldResume
if statement because that
if
condition is not true when the
.Ended
interruption occurs.

Answer

Xcode 7.3.1 • Swift 2.3.1

let audioSession = AVAudioSession.sharedInstance()
do {
    try audioSession.setCategory(AVAudioSessionCategoryPlayback, withOptions: .MixWithOthers)
    print("AVAudioSession Category Playback OK")
    do {
        try audioSession.setActive(true)
        print("AVAudioSession is Active")
    } catch let error as NSError {
        print(error.localizedDescription)
    }
} catch let error as NSError {
    print(error.localizedDescription)
}

func handleInterruption(notification: NSNotification) {
    print("handleInterruption")
    guard let value = (notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? NSNumber)?.unsignedIntegerValue,
    let interruptionType =  AVAudioSessionInterruptionType(rawValue: value)
    else {
        print("notification.userInfo?[AVAudioSessionInterruptionTypeKey]", notification.userInfo?[AVAudioSessionInterruptionTypeKey])
        return }
    switch interruptionType {
    case .Began:
        print("began")
        audioPlayer.pause()
        print("audioPlayer.playing", audioPlayer.playing)
        do {
            try audioSession.setActive(false)
            print("AVAudioSession is inactive")
        } catch let error as NSError {
            print(error.localizedDescription)
        }
        // player is paused and session is inactive. need to update UI)
    default :
        print("ended")
        if let optionValue = (notification.userInfo?[AVAudioSessionInterruptionOptionKey] as? NSNumber)?.unsignedIntegerValue where AVAudioSessionInterruptionOptions(rawValue: optionValue) == .ShouldResume {
            print("should resume")
            // ok to resume playing, re activate session and resume playing
            do {
                try audioSession.setActive(true)
                print("AVAudioSession is Active again")
                audioPlayer.play()
            } catch let error as NSError {
                print(error.localizedDescription)
            }
            // need to update UI

        }
    }
}
Comments