27

New to iOS development, so here goes. I have an app that is playing audio - I'm using AVAudioPlayer to load single files by name in the app's assets. I don't want to query the user's library, only the files provided. Works great, but, I want the user to be able to pause and adjust volume from the lock screen.

func initAudioPlayer(file:String, type:String){
    let path = NSBundle.mainBundle().pathForResource(file, ofType: type)!
    let url = NSURL(fileURLWithPath: path)
    let audioShouldPlay = audioPlaying()
    do{
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        try AVAudioSession.sharedInstance().setActive(true)
        let audioPlayer:AVAudioPlayer = try AVAudioPlayer(contentsOfURL: url)
        audioPlayer.volume = slider.value
        audioPlayer.numberOfLoops = -1
        audioPlayer.prepareToPlay()
        if(audioShouldPlay){
            audioPlayer.play()
//                let mpic = MPNowPlayingInfoCenter.defaultCenter()
//                mpic.nowPlayingInfo = [MPMediaItemPropertyTitle:"title", MPMediaItemPropertyArtist:"artist"]
        }
    }
    catch{}
}

My use of AVAudioSession and MPNowPlayingInfoCenter were just experiments from reading other related posts.

Background mode is enabled for audio in my app's plist file

Jason Roman
  • 8,146
  • 10
  • 35
  • 40
nbpeth
  • 2,967
  • 4
  • 24
  • 34
  • What's the question / problem? – matt Jan 09 '16 at 00:09
  • The question is how I get audio controls to the lock screen. I've seen that the MPNowPlayingInfoCenter nowPlayingInfo is how one passes song meta data to the lock screen / control center, but I'm missing on how to tie it in with AVAudioPlayer – nbpeth Jan 09 '16 at 00:11
  • There are already audio controls on the lock screen. – matt Jan 09 '16 at 00:12
  • 1
    No controls appear for the audio playing for my app – nbpeth Jan 09 '16 at 00:14
  • However, I have required background modes set to 'app plays audio or streams audio..'. Something interesting is that audio plays in the background when using the simulators in xcode, but when I deploy the app to an actual device it doesn't work. maybe there's something missing in the background mode configuration, but I can't seem to find anything outside of adding that property to info.plist – nbpeth Jan 09 '16 at 00:16
  • This has nothing, per se, to do with background mode. If you are remote control target, that keeps working if you are also playing in the background, but playing in the background does not magically make you the remote control target. – matt Jan 09 '16 at 00:18

5 Answers5

36

You need to invoke beginReceivingRemoteControlEvents() otherwise it will not work on the actual device.

Swift 3.1

UIApplication.shared.beginReceivingRemoteControlEvents()

If you would like to specify custom actions for the MPRemoteCommandCenter:

let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.nextTrackCommand.isEnabled = true
commandCenter.nextTrackCommand.addTarget(self, action:#selector(nextTrackCommandSelector))

edit/update:

Apple has a sample project showing how to Becoming a Now Playable App

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • 8
    final answer, `UIApplication.sharedApplication().beginReceivingRemoteControlEvents()` and `AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)` – nbpeth Jan 09 '16 at 18:20
  • 2
    where exactly should these lines be placed? I am unable to get any lock screen controls to display for my app although audio playback works fine – MikeG Mar 07 '17 at 18:43
  • 1
    @mike in didFinishLaunchOptions of appdelege. Its Late but this link is useful https://developer.apple.com/library/content/samplecode/MPRemoteCommandSample/Introduction/Intro.html – Surjeet Rajput Jun 30 '17 at 13:33
  • 1
    according to Readme in sample code provided by @commando24, "When using `MPRemoteCommandCenter` you do not need to call `UIApplication.beginReceivingRemoteControlEvents()`." – kikeenrique Nov 13 '18 at 16:48
  • 2
    In iOS 7.1 and later, use the shared MPRemoteCommandCenter object to register for remote control events. You do not need to call `beginReceivingRemoteControlEvents ` method when using the shared command center object. – Jakub Truhlář Jan 24 '19 at 13:31
  • hey, i am having the same problem and no luck solving it https://stackoverflow.com/questions/59886747/ios-create-audio-playback-notification – medyas Jan 24 '20 at 11:50
19

To implement this functionality, use the Media Player framework’s MPRemoteCommandCenter and MPNowPlayingInfoCenter classes with AVPlayer.

import MediaPlayer
import AVFoundation

// Configure AVPlayer
var player = AVPlayer()

Configure the Remote Command Handlers

Defines a variety of commands in the form of MPRemoteCommand objects to which you can attach custom event handlers to control playback in your app.

    func setupRemoteTransportControls() {
    // Get the shared MPRemoteCommandCenter
    let commandCenter = MPRemoteCommandCenter.shared()

    // Add handler for Play Command
    commandCenter.playCommand.addTarget { [unowned self] event in
        if self.player.rate == 0.0 {
            self.player.play()
            return .success
        }
        return .commandFailed
    }

    // Add handler for Pause Command
    commandCenter.pauseCommand.addTarget { [unowned self] event in
        if self.player.rate == 1.0 {
            self.player.pause()
            return .success
        }
        return .commandFailed
    }
}

Provide Display Metadata

Provide a dictionary of metadata using the keys defined by MPMediaItem and MPNowPlayingInfoCenter and set that dictionary on the default instance of MPNowPlayingInfoCenter.

func setupNowPlaying() {
    // Define Now Playing Info
    var nowPlayingInfo = [String : Any]()
    nowPlayingInfo[MPMediaItemPropertyTitle] = "My Movie"

    if let image = UIImage(named: "lockscreen") {
        nowPlayingInfo[MPMediaItemPropertyArtwork] =
            MPMediaItemArtwork(boundsSize: image.size) { size in
                return image
        }
    }
    nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = playerItem.currentTime().seconds
    nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = playerItem.asset.duration.seconds
    nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate

    // Set the metadata
    MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}

For more information refer Apples official Documentation

Sreekuttan
  • 1,579
  • 13
  • 19
4
func myplayer(file:String, type:String){
let path = NSBundle.mainBundle().pathForResource(file, ofType: type)!
let url = NSURL(fileURLWithPath: path)
let audioShouldPlay = audioPlaying()
do{
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
    try AVAudioSession.sharedInstance().setActive(true)
    let audioPlayer:AVAudioPlayer = try AVAudioPlayer(contentsOfURL: url)
    audioPlayer.volume = slider.value
    audioPlayer.numberOfLoops = -1
    audioPlayer.prepareToPlay()
    if(audioShouldPlay){
        audioPlayer.play()
//                let mpic = MPNowPlayingInfoCenter.defaultCenter()
//                mpic.nowPlayingInfo = [MPMediaItemPropertyTitle:"title", 
MPMediaItemPropertyArtist:"artist"]
        }
    }
    catch{}
}
lxg
  • 12,375
  • 12
  • 51
  • 73
2

There are already audio controls on the lock screen (the "remote control" interface). If you want them to control your app's audio, you need to make your app the remote control target, as described in Apple's documentation.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    I explain one way to do this in my book; example code here: https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch14p643ducking/ch27p912ducking/ViewController.swift Or you can use MPRemoteCommandCenter. – matt Jan 09 '16 at 00:15
  • haha, alright. I was actually looking at this earlier. I'll explore the remote control end of it. Any thoughts on why background mode might be working in simulator but not on an actual iphone – nbpeth Jan 09 '16 at 00:18
  • Simulator is unreliable with regard to background modes - do not use it for any kind of experimentation in that regard. This is a known, acknowledged bug. – matt Jan 09 '16 at 00:19
  • Link above is broke unfortunately – powerj1984 May 10 '17 at 13:39
2

I Have this problem. You need only

audioSession.setCategory(.playback, mode: .default) or 
audioSession.setCategory(.playback, mode: .default, options: 
                                               .init(rawValue: 0)) 
sai saran
  • 737
  • 9
  • 32