Do not trigger remoteControlReceived in UIView after load the view - ios

I implement an audio player using AVAudioPlayer as the following.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient, with: AVAudioSessionCategoryOptions.allowBluetooth)
try? AVAudioSession.sharedInstance().setActive(true)
if let path = Bundle.main.path(forResource: "LadyGaga-MillionReasons", ofType: "mp3") {
let url = URL(string:path)
self.audioPlayer = try? AVAudioPlayer(contentsOf: url!)
self.audioPlayer?.prepareToPlay()
}
self.becomeFirstResponder()
UIApplication.shared.beginReceivingRemoteControlEvents()
}
override var canBecomeFirstResponder: Bool{
get{
return true
}
}
override var canResignFirstResponder: Bool{
get{
return true
}
}
#IBAction func btnPlay_TouchUpInsde(_ sender: Any) {
if (self.audioPlayer?.isPlaying)! {
self.audioPlayer?.pause()
self.btnPlay.setTitle("Play", for: .normal)
}else{
self.audioPlayer?.play()
self.btnPlay.setTitle("Pause", for: .normal)
}
}
override func remoteControlReceived(with event: UIEvent?) {
if let e = event , e.type == .remoteControl {
if e.subtype == UIEventSubtype.remoteControlPause {
self.audioPlayer?.pause()
}else if(e.subtype == .remoteControlPlay){
self.audioPlayer?.play()
}else if(e.subtype == .remoteControlTogglePlayPause){
if self.audioPlayer!.isPlaying {
self.audioPlayer?.pause()
}else{
self.audioPlayer?.play()
}
}
}
}
}
The app is launched, then click the play button, the audio plays ok.
Then I pause the audio by a headset,and all works ok.
Another situation:
The app is launched, then I want to start the audio by a headset, it does not work.
It seems to that the view is not the first responder before I click the button, even I add self.becomeFirstResponder in init function.
Who know the why the app can not get the remoteControlReceived event when do not click the button.
I implement a sample. https://github.com/leogeng/AuidoTest.git

Related

How to turn off Background Music using a Toggle Switch in Xcode using Swift?

I am playing background music in my app from the App delegate once the app launches. Now, In my 2nd V.C. I have set up a toggle switch to turn om/off the background music. But, whenever I am running the follwing code, my app is crashing giving me this error:-
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Could anyone please let me know how can I implement the following task in my V.C. Would appreciate your help! Thanks:)
**App Delegate**
let vc = SecondViewController()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
vc.playMusic()
return true
}
**Second View Controller**
import UIKit
import AVFoundation
class SecondViewController: UIViewController {
#IBOutlet weak var musicToggleSwitch: UISwitch!
var music: AVAudioPlayer!
let vc_1 = ViewController()
override func viewDidLoad() {
super.viewDidLoad()
self.musicToggleSwitch.setOn(UserDefaults.standard.bool(forKey: "musicToggleState"), animated: true)
}
#IBAction func musicToggleSwitch(_ sender: UISwitch) {
if (musicToggleSwitch.isOn == true) {
if (music.isPlaying == false) {
music.play()
}
}
else {
if (music.isPlaying == true) {
music.stop()
}
}
UserDefaults.standard.set(sender.isOn, forKey: "musicToggleState")
}
func playMusic() {
if let musicURL = Bundle.main.url(forResource: "Music", withExtension: "mp3") {
if let audioPlayer = try? AVAudioPlayer(contentsOf: musicURL) {
music = audioPlayer
music.numberOfLoops = -1
music.play()
}
}
}
}
This should solve your issues.
If it still doesn't play, add some breakpoints in the creation block of the music variable to see what goes wrong.
class SecondViewController: UIViewController {
#IBOutlet weak var musicToggleSwitch: UISwitch!
var music: AVAudioPlayer? = {
guard let musicURL = Bundle.main.url(forResource: "Music", withExtension: "mp3") else {
return nil
}
let audioPlayer = try? AVAudioPlayer(contentsOf: musicURL)
audioPlayer?.numberOfLoops = -1
return audioPlayer
}()
override func viewDidLoad() {
super.viewDidLoad()
self.musicToggleSwitch.setOn(UserDefaults.standard.bool(forKey: "musicToggleState"), animated: true)
}
#IBAction func musicToggleSwitch(_ sender: UISwitch) {
guard let music = music, sender.isOn != music.isPlaying else {
return
}
if sender.isOn {
music.play()
} else {
music.stop()
}
UserDefaults.standard.set(sender.isOn, forKey: "musicToggleState")
}
func playMusic() {
music?.play()
}
}
The problem is this line:
let vc = SecondViewController()
That is not the same SecondViewController that you see in your interface. So playMusic is called on the wrong view controller, and on the view controller you can see music remains nil and you crash.

Storyboard segue causes AVAudioEngine to crash

I am working with an AVAudioUnitSampler that is attached to an AVAudioEngine within my app. I've gotten everything to work well except when I segue out of the view controller that the sampler is instantiated in. I get a crash with an error that says:
Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: outputNode'
I'm guessing that this is because the engine is getting interrupted somehow when I segue back to my previous VC, not sure!
I've tried to stop the engine and also the sequencer inside of viewWillDisappear, but it still crashes.
If I use a UIButton show segue to the previous VC,it sort of works, but I get a crash with an unwind segue and with the current navigation bar segue.
I'm a newbie, so hopefully I've explained this well enough!
I am getting to this VC from a segue triggered from a table view on the previous VC.
Here is the code for the VC in question:
import UIKit
import AVFoundation
class PlayerViewController: UIViewController {
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var playPauseButton: UIButton!
#IBOutlet weak var musicView: UIImageView!
let allSounds = SoundBankOnAndOff()
var currentSoundFile: OnandOff?
var engine = AVAudioEngine()
var sampler = AVAudioUnitSampler()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.navigationController?.isNavigationBarHidden = false
setupSequencer()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
engine.stop()
sequencer.stop()
}
override func viewDidLoad() {
super.viewDidLoad()
descriptionLabel.text = exercises[myIndex]
musicView.image = musicNotes[myIndex]
engine = AVAudioEngine()
sampler = AVAudioUnitSampler()
engine.attach(sampler)
engine.connect(sampler, to: engine.mainMixerNode, format: nil)
loadSF2PresetIntoSampler(preset: 0)
startEngine()
setSessionPlayback()
}
func setSessionPlayback() {
let audioSession = AVAudioSession.sharedInstance()
do {
try
audioSession.setCategory(AVAudioSessionCategoryPlayback, with:
AVAudioSessionCategoryOptions.mixWithOthers)
} catch {
print("couldn't set category \(error)")
return
}
do {
try audioSession.setActive(true)
print("category is active")
} catch {
print("couldn't set category to active \(error)")
return
}
}
func startEngine() {
if engine.isRunning {
print("audio engine has already started")
return
}
do {
try engine.start()
print("audio engine started")
} catch {
print("oops \(error)")
print("could not start audio engine")
}
}
var sequencer:AVAudioSequencer!
func setupSequencer() {
let allSounds = SoundBankOnAndOff()
let currentSoundFile = allSounds.list[myIndex]
self.sequencer = AVAudioSequencer(audioEngine: engine)
let options = AVMusicSequenceLoadOptions.smfChannelsToTracks
if let fileURL = Bundle.main.urls(forResourcesWithExtension: "mid", subdirectory: "On & Off MIDI") {
do {
try sequencer.load(from: (currentSoundFile.soundFile), options: options)
print("loaded \(fileURL)")
} catch {
print("something messed up \(error)")
return
}
}
sequencer.prepareToPlay()
}
func play() {
if sequencer.isPlaying {
stop()
}
sequencer.currentPositionInBeats = TimeInterval(0)
do {
try sequencer.start()
} catch {
print("cannot start \(error)")
}
}
func stop() {
sequencer.stop()
}
func loadSF2PresetIntoSampler(preset:UInt8) {
guard let bankURL = Bundle.main.url(forResource: "Pad Sounds", withExtension: "sf2") else {
print("could not load sound font")
return
}
do {
try sampler.loadSoundBankInstrument(at: bankURL, program: preset,bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB),bankLSB: UInt8(kAUSampler_DefaultBankLSB))
} catch {
print("error loading sound bank instrument")
}
}
#IBAction func playButtonPressed(sender: UIButton) {
if sender.currentTitle == "PLAY"{
play()
sender.setTitle("STOP", for: .normal)
} else if sender.currentTitle == "STOP" {
sender.setTitle("PLAY", for: .normal)
stop()
}
}
#IBAction func BackButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "unwindToDetailVC", sender: self)
}
}
This code is similar to yours and doesn't crash when segueing or unwinding:
class MP3PlayerVC: UIViewController {
let audioEngine = AVAudioEngine()
let audioPlayer = AVAudioPlayerNode()
let distortion = AVAudioUnitDistortion()
override func viewDidLoad() {
super.viewDidLoad()
audioEngine.attach(audioPlayer)
audioEngine.attach(distortion)
audioEngine.connect(audioPlayer, to: distortion, format: nil)
audioEngine.connect(distortion, to: audioEngine.outputNode, format: nil)
}
#IBAction func buttonClicked(_ sender: UIButton) {
if let filePath = Bundle.main.path(forResource: "test", ofType: "mp3") {
let url = URL.init(fileURLWithPath: filePath)
if let audioFile = try? AVAudioFile(forReading: url) {
if audioPlayer.isPlaying {
audioPlayer.stop()
audioEngine.stop()
} else {
audioPlayer.scheduleFile(audioFile, at: nil, completionHandler: nil)
do {
try audioEngine.start()
audioPlayer.play()
} catch {
print("something went wrong")
}
}
}
}
}
}

How to track when song is finished AVAudioPlayer

I want to track when playing song is finished. I tried different solutions from the web but they could not solve my problem.
I implemented audioPlayerDidFinishPlaying method but it is not working.
How can I understand if playing song is finished?
I am playing songs with playSound function
playSound func:
func playSound(name: String ) {
guard let url = Bundle.main.url(forResource: name, withExtension: "mp3") else {
print("url not found")
return
}
do {
/// this codes for making this app ready to takeover the device audio
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
/// change fileTypeHint according to the type of your audio file (you can omit this)
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3)
// no need for prepareToPlay because prepareToPlay is happen automatically when calling play()
player!.play()
} catch let error as NSError {
print("error: \(error.localizedDescription)")
}
}
audioPlayerDidFinishPlaying func:
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
print("finished")//It is not working, not printing "finished"
}
How can I solve my problem? How to track when playing song is finished
EDIT: I am adding whole code.
//
// ViewController.swift
import UIKit
import SwiftVideoBackground
import AudioToolbox
import AVFoundation
class ViewController: UIViewController,AVAudioPlayerDelegate {
var player: AVAudioPlayer?
#IBOutlet weak var backgroundVideo: BackgroundVideo!
#IBOutlet weak var initialLabel: UILabel!
#IBOutlet weak var statementLabel: UILabel!
var mp3: [String] = ["turk_milleti_demokrattir","xyz"]
var fav: [String] = ["0","0"]
var name: [String] = ["Türk milleti demokrattır","xy"]
var toggleState = 1
#IBOutlet weak var playB: UIButton!
var counter = 0
var duration = 0.1
override func viewDidLoad() {
super.viewDidLoad()
player?.delegate = self
playB.setImage(UIImage(named: "playbtn.png"), for: .normal)
statementLabel.text = name[counter]
backgroundVideo.createBackgroundVideo(name: "abc", type: "mp4")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func likeButton(_ sender: Any) {
fav[counter] = "1"
print(fav[0...1])
}
#IBAction func playButton(_ sender: Any) {
let name = mp3[counter]
playSound(name: name)
let playBtn = sender as! UIButton
if toggleState == 1 {
player?.play()
toggleState = 2
playBtn.setImage(UIImage(named: "pausebtn.png"), for: .normal)
} else {
player?.pause()
toggleState = 1
playBtn.setImage(UIImage(named:"playbtn.png"),for: .normal)
}
}
#IBAction func nextButton(_ sender: Any) {
counter = counter + 1
if counter == mp3.count {
counter = 0
}
toggleState = 2
playB.setImage(UIImage(named: "pausebtn.png"), for: .normal)
playSound(name: mp3[counter])
statementLabel.text = name[counter]
}
func playSound(name: String ) {
guard let url = Bundle.main.url(forResource: name, withExtension: "mp3") else {
print("url not found")
return
}
do {
/// this codes for making this app ready to takeover the device audio
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
/// change fileTypeHint according to the type of your audio file (you can omit this)
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3)
// no need for prepareToPlay because prepareToPlay is happen automatically when calling play()
player!.play()
} catch let error as NSError {
print("error: \(error.localizedDescription)")
}
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
print("finished")//It is not working, not printing "finished"
}
}
I solved my problem with help of Leo Dabus.
I changed my edited code. I moved player?.delegate = self
to playSound func. Finally, it is working.
playSound & audioPlayerDidFinishPlaying function:
func playSound(name: String ) {
guard let url = Bundle.main.url(forResource: name, withExtension: "mp3") else {
print("url not found")
return
}
do {
/// this codes for making this app ready to takeover the device audio
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
/// change fileTypeHint according to the type of your audio file (you can omit this)
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3)
player?.delegate = self
// no need for prepareToPlay because prepareToPlay is happen automatically when calling play()
player!.play()
} catch let error as NSError {
print("error: \(error.localizedDescription)")
}
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
print("finished")//It is working now! printed "finished"!
}
Do not forget to add AVAudioPlayerDelegate to ViewController!
class ViewController: UIViewController,AVAudioPlayerDelegate {
You are not setting the player's delegate correctly.
In viewDidLoad, your player is going to be nil, so this line:
player?.delegate = self
Will do nothing (The question mark is optional chaining, so if player == nil, it does nothing.)
You need to set the delegate after loading the player.

Swift - Stop avaudioplayer

I am trying to build a soundboard into an app and have figured out an efficient way of using tags to control playing the sounds. However I am now trying to integrate a pause button that can be used with the .stop() method on the AVAudioPlayer however I get an error with my current code:
EXC_BAD_ACCESS
This is what I am using at the moment, any ideas?
import UIKit
import AVFoundation
let soundFilenames = ["sound","sound2","sound3"]
var audioPlayers = [AVAudioPlayer]()
class SecondViewController: UIViewController {
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
for sound in soundFilenames {
do {
let url = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(sound, ofType: "mp3")!)
let audioPlayer = try AVAudioPlayer(contentsOfURL: url)
audioPlayers.append(audioPlayer)
} catch {
//Catch error thrown
audioPlayers.append(AVAudioPlayer())
}
}
}
#IBAction func buttonPressed(sender: UIButton) {
let audioPlayer = audioPlayers[sender.tag]
audioPlayer.play()
}
#IBAction func stop(sender: UIButton) {
audioPlayer.stop()
}
}
Your audioPlayer in stop function is not the playing player. You should assign it in buttonPressed function.
#IBAction func buttonPressed(sender: UIButton) {
audioPlayer = audioPlayers[sender.tag]
audioPlayer.play()
}
By the way, You can mark audioPlayer as a "?" property, it will be more efficient when init this Controller.
class SecondViewController: UIViewController {
var audioPlayer: AVAudioPlayer?
let enableMuiltPlayers = false
....
#IBAction func buttonPressed(sender: UIButton) {
if sender.tag < audioPlayers.count else {
print("out of range")
return
}
if enableMuiltPlayers {
audioPlayers[sender.tag].play()
} else {
audioPlayer?.stop()
//set the current playing player
audioPlayer = audioPlayers[sender.tag]
audioPlayer?.play()
}
}
#IBAction func stop(sender: UIButton) {
let wantToStopAll = false
if enableMuiltPlayers && wantToStopAll {
stopAll()
} else {
audioPlayer?.stop()
}
audioPlayer = nil
}
}
to stop all:
fun stopAll() {
for player in audioPlayers {
player.stop()
}
}
Your code may have other faults, but there's one thing sure:
You should not instantiate AVAudioPlayer using default initializer AVAudioPlayer().
Change this line:
var audioPlayer = AVAudioPlayer()
to:
var playingAudioPlayer: AVAudioPlayer?
And change this part:
} catch {
//Catch error thrown
audioPlayers.append(AVAudioPlayer())
}
to something like this:
} catch {
//Catch error thrown
fatalError("Sound resource: \(sound) could not be found")
}
(The latter part is very important to solve the issue. But I found it had become just a duplicate of some part of Hao's answer after I edited it...)
And start method:
#IBAction func start(sender: UIButton) {
let audioPlayer = audioPlayers[sender.tag]
audioPlayer.start()
playingAudioPlayer = audioPlayer
}
And stop should be:
#IBAction func start(sender: UIButton) {
playingAudioPlayer?.stop()
}
if audioPlayer != nil {
if audioPlayer.playing {
audioPlayer.stop()
}
}

Let AVPlayer continue playing when pressing the 'back' button on the UINavigation Controller in Swift 2

I’m developing a Radio Streaming app, it streams OK. Also it plays in background when press on the ‘Home’ button and ‘lock’ button.
The application is embedded into a UINavigationController and when I press on the ‘back’ button in the UINavigationController it stops playing.
My question is: How do I let the UIViewController that contain the AVPlayer to remain active when pressing the ‘back’ button in the navigation controller so that the AVPlayer continue streaming?
My code
import UIKit
import AVFoundation
import MediaPlayer
import Foundation
class RadioFunctionViewController: UIViewController {
#IBOutlet var playButton: UIButton!
#IBOutlet var statusLabel: UILabel!
var player:AVPlayer = AVPlayer()
private let ObservatingKeyPath = "currentItem.status"
private let PlayerStatusObservingContext = UnsafeMutablePointer<Int>(bitPattern: 1)
private var playingState:Bool = false
override func viewDidLoad() {
super.viewDidLoad()
setStatus(4)
getAudioData("http://184.107.179.162:7546/;")
playingState = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonPressed(sender: AnyObject)
{
toggle()
}
func getAudioData(audioURL:String)
{
player = AVPlayer(URL: NSURL(string: audioURL)!)
player.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.Initial, context: nil)
}
func setStatus(rawValue:Int)
{
if rawValue == 1
{
statusLabel.textColor = UIColor.blueColor()
statusLabel.text = "Ready for Streaming"
}else if rawValue == 2
{
statusLabel.textColor = UIColor.redColor()
statusLabel.text = "Failed"
}else if rawValue == 0
{
statusLabel.textColor = UIColor.redColor()
statusLabel.text = "Failed to load data"
}else if rawValue == 3
{
statusLabel.textColor = UIColor.blueColor()
statusLabel.text = "Streaming"
}else if rawValue == 4
{
statusLabel.textColor = UIColor.purpleColor()
statusLabel.text = "Gather data..."
}
print("The raw value send is: \(rawValue)")
}
func audioBackgroundPlayback()
{
do{
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
}catch {
print("Could not play audio in the background")
}
if (NSClassFromString("MPNowPlayingInfoCenter") != nil)
{
let artWorkImage = MPMediaItemArtwork(image: UIImage(named: "ws")!)
let songInfo2: [String: AnyObject] = [MPMediaItemPropertyTitle: "Wide Streamings ABC Edition", MPMediaItemPropertyArtist: "Rumbera Network", MPMediaItemPropertyAlbumTitle: "107.9 FM", MPMediaItemPropertyArtwork: artWorkImage]
MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = songInfo2
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
}
}
func toggle()
{
if playButton.titleLabel?.text == "Play"
{
print("The play option is chosen")
playRadio()
}else{
print("The pause option is chosen")
pauseRadio()
}
}
func playRadio()
{
player.play()
setStatus(3)
playButton.setTitle("Pause", forState: UIControlState.Normal)
audioBackgroundPlayback()
}
func pauseRadio()
{
player.pause()
playButton.setTitle("Play", forState: UIControlState.Normal)
}
override func remoteControlReceivedWithEvent(event: UIEvent?) {
if event?.type == UIEventType.RemoteControl
{
if event?.subtype == UIEventSubtype.RemoteControlPlay
{
toggle()
}else if event?.subtype == UIEventSubtype.RemoteControlPause
{
pauseRadio()
}else if event?.subtype == UIEventSubtype.RemoteControlTogglePlayPause
{
toggle()
}
}
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>)
{
if (keyPath!.containsString("status"))
{
if player.status == AVPlayerStatus.ReadyToPlay
{
player.prerollAtRate(0.001, completionHandler: {(succes:Bool)-> Void in
if succes{
self.setStatus(1)
self.setStatus(3)
self.playRadio()
}else{
self.setStatus(1)
self.setStatus(2)
}
})
}else if player.status == AVPlayerStatus.Failed{
self.setStatus(2)
}else if player.status == AVPlayerStatus.Unknown
{
self.setStatus(0)
}
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.navigationBarHidden = false
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.navigationBarHidden = false
if playingState == true
{
audioBackgroundPlayback()
player.removeObserver(self, forKeyPath: "status")
print("The AVPlayer is playing in background")
}else{
player.removeObserver(self, forKeyPath: "status")
print("The view Dissapear")
}
}
Hope someone could help me out with this one
Thanks in advance
AVPlayer will continue to work as long as it is active. Once the referencing UIViewController is released by popping back AVPlayer will also be discarded from memory.
I would advise you to create a singleton player class and create APIs to start/stop/play/pause AVPlayer in that. Now, you can access it globally from everywhere in your app.
EDIT: For OP convenience (a sample to start with):
class MyAVPlayer {
static let sharedInstance = MyAVPlayer()
var player:AVPlayer = AVPlayer()
func play() {
// Put play code here
}
func playWithURL(url : NSURL) {
// Put play code here
}
}
Call it like (from anywhere in your application):
MyAVPlayer.sharedInstance.playWithURL(myURL)
The problem here is that the player is Embed in your controller
var player:AVPlayer = AVPlayer()
So when you press the back button, the controller is popped and deallocated and your player with it.
What you have to do is to put the player property elsewhere (AppDelegate, Custom navigation controller for exemple) to keep a reference on it that'll keep it alive.
I manage to fix it by doing some modification to the AppDelegate Class.
The modification into the AppDelegate:
var player:AVPlayer = AVPlayer()
internal var avPlayerUpdateNotification = NSNotificationCenter.defaultCenter()
let notificationStateupdate = "RadioStationChangeUpdate"
let radioStationChangeNSString:NSString = "RadioStationChangeNotification"
private var isPlaying:Bool = false
private var selectedRadioStation:String = ""
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
// Override point for customization after application launch.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setNewSelectedRadioStation:", name: radioStationChangeNSString as String, object: nil)
return true
}
Other functions that has been added into the AppDelegate Class
func streamAudio(audioLink:String)
{
player = AVPlayer(URL: NSURL(string: audioLink)!)
player.play()
isPlaying = true
}
func play()
{
player.play()
isPlaying = true
}
func pause()
{
player.pause()
isPlaying = false
}
func getPlayerState() -> Bool
{
return isPlaying
}
func setCurrentSelectedRadioStation(selectedStation:String)
{
self.selectedRadioStation = selectedStation
}
func getCurrentSelectedRadioStation() -> String
{
return selectedRadioStation
}
func setNewSelectedRadioStation(notification: NSNotification)
{
let radioStation = notification.object!
if (radioStation.containsString("Rumbera network"))
{
if(selectedRadioStation == radioStation as! String)
{
print("Rumbera Network is already playing")
}else{
print("Rumbera Network is selected in AppDelegate")
streamAudio("http://184.107.179.162:7546/;")
setCurrentSelectedRadioStation(radioStation as! String)
}
}else if (radioStation.containsString("RocKorsow"))
{
if(selectedRadioStation == radioStation as! String)
{
print("RocKorsow is already playing")
}else{
print("RocKorsow is selected in AppDelegate")
streamAudio("http://youngfreshfast.serverroom.us:9166")
setCurrentSelectedRadioStation(radioStation as! String)
}
}else if (radioStation.containsString("Pause"))
{
pause()
}else if (radioStation.containsString("Play"))
{
play()
}else{
print("Nothing is found")
}
}
The class that handles the AVPlayer:
import UIKit
import AVFoundation
import MediaPlayer
import Foundation
class RadioFunctionViewController: UIViewController {
#IBOutlet var playButton: UIButton!
var player:AVPlayer = AVPlayer()
private let ObservatingKeyPath = "currentItem.status"
private let PlayerStatusObservingContext = UnsafeMutablePointer<Int>(bitPattern: 1)
private var playingState:Bool = false
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
checkPlayerCurrentState()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func performAction(sender: UIButton)
{
let buttonLabel = (sender.titleLabel?.text)!
switch buttonLabel
{
case "Play":
print("The AVPlayer is playing")
NSNotificationCenter.defaultCenter().postNotificationName("RadioStationChangeNotification", object: NSString(string: "Play"))
playButton.setTitle("Pause", forState: UIControlState.Normal)
case "Pause":
print("The AVPlayer is pause")
NSNotificationCenter.defaultCenter().postNotificationName("RadioStationChangeNotification", object: NSString(string: "Pause"))
playButton.setTitle("Play", forState: UIControlState.Normal)
default:
break
}
}
func checkPlayerCurrentState()
{
let player_state = getPlayerState()
if player_state == true
{
print("The AVPlayer is playing")
playButton.setTitle("Pause", forState: UIControlState.Normal)
}else{
print("The AVPlayer is not playing")
playButton.setTitle("Play", forState: UIControlState.Normal)
}
}
func getPlayerState() -> Bool
{
let state = appDelegate.getPlayerState()
return state
}
func audioBackgroundPlayback()
{
do{
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
}catch {
print("Could not play audio in the background")
}
}
Hope my solution could help someone that encounter the same problem.
Thanks guys for your feedback.

Resources