iosswiftmpmusicplayercontrollermpmediapickercontroller

How to play music in background in Swift with MPMediaPickerController chosen songs?


I am trying to allow the user to pick a song from their library and play it across different view controllers. I am able to pick songs currently and play them, but I don't know how to play them in the background. Thanks!

class ViewController: UIViewController,
MPMediaPickerControllerDelegate, AVAudioPlayerDelegate {


    var myMusicPlayer: MPMusicPlayerController?
    var mediaPicker: MPMediaPickerController?
    var backgroundMusicPlayer:AVAudioPlayer = AVAudioPlayer()


    @IBAction func musicBtn(sender: AnyObject) {
        displayMediaPickerAndPlayItem()
    }


    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

    }


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


    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */



    func musicPlayerStateChanged(notification: NSNotification){

        print("Player State Changed")

        /* Let's get the state of the player */
        let stateAsObject =
            notification.userInfo!["MPMusicPlayerControllerPlaybackStateKey"]
                as? NSNumber

        if let state = stateAsObject{

            /* Make your decision based on the state of the player */
            switch MPMusicPlaybackState(rawValue: state.integerValue)!{
            case .Stopped:
                /* Here the media player has stopped playing the queue. */
                print("Stopped")
            case .Playing:
                /* The media player is playing the queue. Perhaps you
                 can reduce some processing that your application
                 that is using to give more processing power
                 to the media player */
                print("Paused")
            case .Paused:
                /* The media playback is paused here. You might want
                 to indicate by showing graphics to the user */
                print("Paused")
            case .Interrupted:
                /* An interruption stopped the playback of the media queue */
                print("Interrupted")
            case .SeekingForward:
                /* The user is seeking forward in the queue */
                print("Seeking Forward")
            case .SeekingBackward:
                /* The user is seeking backward in the queue */
                print("Seeking Backward")
            }

        }
    }

    func nowPlayingItemIsChanged(notification: NSNotification){

        print("Playing Item Is Changed")

        let key = "MPMusicPlayerControllerNowPlayingItemPersistentIDKey"

        let persistentID =
            notification.userInfo![key] as? NSString

        if let id = persistentID{
            /* Do something with Persistent ID */
            print("Persistent ID = \(id)")
        }

    }

    func volumeIsChanged(notification: NSNotification){
        print("Volume Is Changed")
        /* The userInfo dictionary of this notification is normally empty */
    }

    func mediaPicker(mediaPicker: MPMediaPickerController,
                     didPickMediaItems mediaItemCollection: MPMediaItemCollection){

        print("Media Picker returned")

        /* Instantiate the music player */

        myMusicPlayer = MPMusicPlayerController()

        if let player = myMusicPlayer{

            player.beginGeneratingPlaybackNotifications()

            /* Get notified when the state of the playback changes */
            NSNotificationCenter.defaultCenter().addObserver(self,
                                                             selector: "musicPlayerStateChanged:",
                                                             name: MPMusicPlayerControllerPlaybackStateDidChangeNotification,
                                                             object: nil)

            /* Get notified when the playback moves from one item
             to the other. In this recipe, we are only going to allow
             our user to pick one music file */
            NSNotificationCenter.defaultCenter().addObserver(self,
                                                             selector: "nowPlayingItemIsChanged:",
                                                             name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification,
                                                             object: nil)

            /* And also get notified when the volume of the
             music player is changed */
            NSNotificationCenter.defaultCenter().addObserver(self,
                                                             selector: "volumeIsChanged:",
                                                             name: MPMusicPlayerControllerVolumeDidChangeNotification,
                                                             object: nil)

            /* Start playing the items in the collection */
            player.setQueueWithItemCollection(mediaItemCollection)
            player.play()

            /* Finally dismiss the media picker controller */
            mediaPicker.dismissViewControllerAnimated(true, completion: nil)

        }

    }

    func mediaPickerDidCancel(mediaPicker: MPMediaPickerController) {
        /* The media picker was cancelled */
        print("Media Picker was cancelled")
        mediaPicker.dismissViewControllerAnimated(true, completion: nil)
    }

    func stopPlayingAudio(){

        NSNotificationCenter.defaultCenter().removeObserver(self)

        if let player = myMusicPlayer{
            player.stop()
        }

    }

    func displayMediaPickerAndPlayItem(){

        mediaPicker = MPMediaPickerController(mediaTypes: .AnyAudio)

        if let picker = mediaPicker{


            print("Successfully instantiated a media picker")
            picker.delegate = self
            picker.allowsPickingMultipleItems = true
            picker.showsCloudItems = true
            picker.prompt = "Pick a song please..."
            view.addSubview(picker.view)

            presentViewController(picker, animated: true, completion: nil)

        } else {
            print("Could not instantiate a media picker")
        }

    }

}

Solution

  • You can use singleton to achieve it like this:

    import AVFoundation
    
    class MusicHelper {
        static let sharedHelper = MusicHelper()
        var audioPlayer: AVAudioPlayer?
    
        func playBackgroundMusic() {
            let pickedSong = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("userPickedSong", ofType: "mp3")!)
            do {
                audioPlayer = try AVAudioPlayer(contentsOfURL:aSound)
                audioPlayer!.numberOfLoops = -1
                audioPlayer!.prepareToPlay()
                audioPlayer!.play()
            } catch {
                print("Cannot play the file")
            }
        }
    }
    

    And you can use it like this:

    MusicHelper.sharedHelper.playBackgroundMusic()
    

    This is just one example how you can do it. Actually there are more and there are already answers on StackOverflow. But with the way I showed you can call it in any class.