10

I have an app that plays audio on the background using AVPlayer. I use MPNowPlayingInfoCenter to display the song's metadata on the Lock Screen and Control Center. Everything works fine except for one thing.

The remote controls on the Lock Screen and Control Center are those of a podcast app. They don't have forward and previous buttons.

I have a screenshot of how the controls are:

enter image description here

As you can see, I don't have forward and previous buttons.

 override func viewDidLoad() {
    super.viewDidLoad()

    do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
    } catch {
        print(error)
    }
}

@IBAction func play(sender: AnyObject) {

    if isURLAvailable == false {
        return
    }

    if (player!.rate != 0 && player!.error == nil) {
        player!.pause()
    } else {
        player!.play()
    }
        updateImage()
}

func playSong(song: Song) {

    let documentsDirectoryURL =  NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as NSURL?
    let url: NSURL? = documentsDirectoryURL?.URLByAppendingPathComponent(song.fileName)

    let avItem = AVPlayerItem(URL: url!)
    player = AVPlayer(playerItem: avItem)

    player?.play()

    let artworkProperty = MPMediaItemArtwork(image: song.artwork)
        MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = [MPMediaItemPropertyTitle : lblSongName.text!, MPMediaItemPropertyArtist : song.artist, MPMediaItemPropertyArtwork : artworkProperty, MPNowPlayingInfoPropertyDefaultPlaybackRate : NSNumber(int: 1), MPMediaItemPropertyPlaybackDuration : CMTimeGetSeconds((player!.currentItem?.asset.duration)!)]

}

override func remoteControlReceivedWithEvent(event: UIEvent?) {
    print(event!.type)
    if event!.type == UIEventType.RemoteControl {
        if event?.subtype == UIEventSubtype.RemoteControlPlay || event?.subtype == UIEventSubtype.RemoteControlPause {
            play(self)
        }
        if event?.subtype == UIEventSubtype.RemoteControlNextTrack {
            next(self)
        }
        if event?.subtype == UIEventSubtype.RemoteControlPreviousTrack {
            previous(self)
        }
    }
}
JAL
  • 41,701
  • 23
  • 172
  • 300
krypton36
  • 197
  • 2
  • 16
  • It would be useful to see what code you have currently implemented. – Ares Nov 28 '15 at 23:34
  • Actually, those are the controls of an audio book, not a podcast, aren't they? – matt Nov 30 '15 at 00:05
  • Where is the code that allows the lock screen to control _your_ app? None of the code you have shown would do that. So my guess so far would be that the buttons are for some other app (like iBooks). – matt Nov 30 '15 at 00:06
  • @matt see the updated question – krypton36 Nov 30 '15 at 20:28
  • Nope, I'm not seeing any relevant code. – matt Nov 30 '15 at 20:30
  • Nope. You're not making yourself first responder. You have not persuaded me that the lock screen is connecting to your app. – matt Nov 30 '15 at 22:03
  • @matt Even with overriding canBecomeFirstResponder, it seems to have no effect on the app's behavior. – krypton36 Nov 30 '15 at 22:08

1 Answers1

14

Rather than using the UIEvent stream with remoteControlReceivedWithEvent, I would recommend you use MPRemoteCommandCenter to control the previous/next/play/pause actions on the lock screen and control center.

import MediaPlayer

// ...

let commandCenter = MPRemoteCommandCenter.sharedCommandCenter()

commandCenter.previousTrackCommand.enabled = true;
commandCenter.previousTrackCommand.addTarget(self, action: "previousTrack")

commandCenter.nextTrackCommand.enabled = true
commandCenter.nextTrackCommand.addTarget(self, action: "nextTrack")

commandCenter.playCommand.enabled = true
commandCenter.playCommand.addTarget(self, action: "playAudio")

commandCenter.pauseCommand.enabled = true
commandCenter.pauseCommand.addTarget(self, action: "pauseAudio")

Where previousTrack, nextTrack, playAudio, and pauseAudio are all functions in your class where you control the player.

You may need to explicitly disable the skip forward and backward commands as well:

commandCenter.skipBackwardCommand.enabled = false
commandCenter.skipForwardCommand.enabled = false
Mert Celik
  • 665
  • 8
  • 12
JAL
  • 41,701
  • 23
  • 172
  • 300
  • 2
    Elegant but is it relevant? – matt Nov 30 '15 at 22:04
  • I'm glad you answered, Although the problem is still present. Perhaps an OS bug? – krypton36 Nov 30 '15 at 22:05
  • @matt If I recall from the Apple docs, `MPRemoteCommandCenter` is the better way to handle events like this rather than using `remoteControlReceivedWithEvent` (https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/Remote-ControlEvents/Remote-ControlEvents.html). If I'm wrong please feel free to correct me and add your own alternative answer. – JAL Nov 30 '15 at 22:12
  • 1
    @JAL No, I agree with you! But I still think the reason why your suggestion works is that he was never making himself first responder so there was in fact no connection between the lock screen / control center and his app. The MPRemoteCommandCenter registration provides a one-step reliable way to set up the same connection. So when he switched to it, things started working. – matt Nov 30 '15 at 23:49
  • There's a typo: `commandCenter.playCommand.enabled = true commandCenter.previousTrackCommand.addTarget(self, action: "playAudio")` It should say `playCommand.addTarget` instead. – Alan Scarpa May 17 '16 at 19:02