Showing Media Player System Controls on Notification Screen in iOS (Swift)

In this blog, I will guide you through the process of displaying and handling actions of media player.

In order to display media player when playing media content in your iOS application, the application needs to support following things.

  1. Media playback
  2. Remote commands configuration
  3. Now playing info center configuration

  1. To support media playing in background

Configure media session with category “playback” so that application keeps playing music even when in background.

Enable audio mode capability.

2. Configuring Remote commands

MPRemoteCommandCenter is a class which allows interaction between your music application and external accessories/system controls. So whenever you pause a music by your earphones button or you hit next track button from you r car’s audio system or you click an action on media view on notification, all those commands are processed by this class’s shared instance.

This class provides with different MPRemoteCommand objects which you can configure in your application. In this blog, I have targeted the basic ones which are commonly used in every other media application.

To configure a particular command, you need to pass in a closure which is to be executed when that command is triggered. From that closure you have to return an enum case that whether that particular command is a success, disabled etc.

3. Configuring Now Playing Info Center

MPNowPlayingInfoCenter ‘default’ instance provide an interface for displaying the media view on notification screen.

But before we jump into it, we need to focus on difference between timeControlStatus and rate property of AVPlayer.

From iOS 10 onwards, rate property should not be used for observing whether media is playing or not. Sometimes media content might not be enough to play, ie. media play has stalled and is buffering for more content to start playing. Although at that time, its rate is 1 but it does not mean it is playing. As soon as the buffer becomes available it starts playing. Here timeControlStatus property helps you distinguish between whether media has stalled and is waiting for buffer or is it actually playing or is it paused.

For better understanding about this, refer to the below link

Now moving on to displaying media view on notification, MPNowPlayingInfoCenter contains “nowPlayingInfo” dictionary which stores the metadata for currently playing media.

You can setup various metadata properties (such as title, album title, media content picture etc.) which you want to display for a media item.

Keep a global dictionary and set that dictionary as nowPlayingInfo dictionary for MPNowPlayingInfoCenter’s default instance. This is mandatory step to avoid race conditions.

Whenever media item is changed, observe duration key for AVPlayerItem and as soon as its duration is not indefinite, you need to update the entire dictionary in order to reflect view with new media attributes along with media duration you just got. (As shown in the above picture).

Regarding Seek Bar

As soon as you update the dictionary with playback duration (MPMediaItemPropertyPlaybackDuration), the seek time keeps on incrementing by itself.

There are few things you need to handle while configuring seek bar.

  1. When the media is paused, seek time still keeps on incrementing
  2. When the media is seeked to a particular position, it does not reflect in seek bar ui of media notification view.
  3. When the media is in buffering state. As discussed above i.e. rate is 1 and timeControlStatus is waitingToPlayAtSpecifiedRate, seek time still keeps on incrementing.

In order to fix these issues, we need to update two attributes. Later on, we will discuss on when to update these attributes.

MPNowPlayingInfoPropertyElapsedPlaybackTime and MPNowPlayingInfoPropertyPlaybackRate are the two properties which are to be updated.

Setting rate to 0, stops the seek time from incrementing and setting it to 1 does the opposite.

Whenever we will be setting the rate to either 0 or 1, we need to set the elapsed playback duration from avplayer’s currentTime method so that seek bar works in sync with rate state change.

For 1st and 3rd, you need to observe changes to timeControlStatus key of AVPlayer instance and update the attributes.

For 2nd, you need to set the rate to 0 along with elapsed playback duration before seeking has completed and after completion closure of seek method is called, set the rate to 1 along with elapsed playback duration.

If you enjoyed reading this post, please share and recommend it so others can find it .