iOS Music Player on the Lock Screen

Building a music player app and want the system to display the current track information on the device lock screen and in the multimedia controls? You can use a now playing info center to set now-playing information for media being played by your app. This takes care of showing the music info and playback controls on the lock screen. If the user directs playback of your media to Apple TV via AirPlay, the now-playing information appears on the television screen. If the user connects a device to an iPod accessory, such as in a car, the accessory may display now-playing information.

MPNowPlayingInfoCenter is the class that controls this information. The information you can specify includes a subset of the properties available in the media item class (MPMediaItem), as well as some properties specific to this class. You do not have direct control over which information is displayed, or its formatting. You set the values of the now playing info center dictionary according to the information you want to provide to the system. The system, or the connected accessory, handles the information’s display in a consistent manner for all apps.

First, let’s discuss the setup required to register our app as a publisher for now playing information. The first requirement is to start receiving remote control events so the users can control the music from the playback controls on the lock screen. Next, we need to listen to the controls we will be handling and perform functions on our app accordingly based on these remote control events.

private func setupNowPlayingInfoCenter() {
    UIApplication.sharedApplication().beginReceivingRemoteControlEvents();
    MPRemoteCommandCenter.sharedCommandCenter().playCommand.addTargetWithHandler {event in
        self.audioPlayer.resume()
        self.updateNowPlayingInfoCenter()
        return .Success
    }
    MPRemoteCommandCenter.sharedCommandCenter().pauseCommand.addTargetWithHandler {event in
        self.audioPlayer.pause()
        return .Success
    }
    MPRemoteCommandCenter.sharedCommandCenter().nextTrackCommand.addTargetWithHandler {event in
        self.next()
        return .Success
    }
    MPRemoteCommandCenter.sharedCommandCenter().previousTrackCommand.addTargetWithHandler {event in
        self.prev()
        return .Success
    }
}

The next part is actually publishing the information about the current track so that it can be displayed on the UI. Here’s a simple function that I use to update the information whenever a track changes or some important event occurs in the music player.

private func updateNowPlayingInfoCenter(artwork: UIImage? = nil) {
    guard let file = currentItem else {
        MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = [String: AnyObject]()
        return
    }
    if let imageURL = file.album?.imageUrl where artwork == nil {
        Haneke.Shared.imageCache.fetch(URL: imageURL, success: {image in
            self.updateNowPlayingInfoCenter(image)
        })
        return
    }
    MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = [
        MPMediaItemPropertyTitle: file.title,
        MPMediaItemPropertyAlbumTitle: file.album?.title ?? "",
        MPMediaItemPropertyArtist: file.album?.artist?.name ?? "",
        MPMediaItemPropertyPlaybackDuration: audioPlayer.duration,
        MPNowPlayingInfoPropertyElapsedPlaybackTime: audioPlayer.progress
    ]
    if let artwork = artwork {
        MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo?[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(image: artwork)
    }
}

There are several metadata properties that you can specify on the now playing info center and it is recommended to set as many as possible. Note that here we use Haneke for loading images from the network. In case we don’t have a cached image straight away, we can set the information without the artwork and then queue the updates again when we fetch the image.

Published 5 Aug 2016

I build mobile and web applications. Full Stack, Rails, React, Typescript, Kotlin, Swift
Pulkit Goyal on Twitter