Now Playing Info
Traditionally, iOS apps utilize MPRemoteCommandCenter and MPNowPlayingInfoCenter to handle playback controls and integrate with iOS's Now Playing Info.
However, when using a WebView for audio playback, these will not work. WebView controls AudioSession and NowPlayingInfo privately in iOS.
Using navigator.mediaSession
The Media Session API is the web equivalent, and it's what a player page should use instead. Setting navigator.mediaSession.metadata to a MediaMetadata object (title, artist, artwork) populates Now Playing Info.
navigator.mediaSession.metadata = new MediaMetadata({
title: 'Title',
artist: 'Artist',
artwork: [{ src: 'https://example.com/example.png' }]
});To modify play/pause, seek, and navigation controls, register the appropriate handlers on navigator.mediaSession:
navigator.mediaSession.setActionHandler(
'play' |
'pause' |
'previoustrack' |
'nexttrack' |
'seekto',
callback
)These are what should call back into window.boppaPlay/boppaPause/boppaSeek, as well as postEvent for previoustrackCommand/nexttrackCommand.
navigator.mediaSession.setPositionState() drives the scrubber, and you can set navigator.mediaSession.playbackState to 'playing'/'paused' on every play/pause to keep it in sync.
navigator.mediaSession.setPositionState({
duration: 213,
position: 42,
playbackRate: 1.0
});
navigator.mediaSession.playbackState = 'playing';Crucially, Now Playing Info isn't owned by the page as a whole, it's owned by whichever specific HTMLMediaElement on the page holds an active MediaElementSession. If no such element actually exists and is playing, these calls succeed without erroring, but there's no session for iOS to route lock screen commands through.
AudioContext and limitations
Web Audio API is an alternative to a plain <audio> element. AudioContext does participate in Now Playing Info, it owns its own PlatformMediaSession and merges in navigator.mediaSession metadata.
But it has limited functionality with remote commands.
WebKit's AudioContext::didReceiveRemoteControlCommand() only handles Play/Pause, and handles them natively, by resuming/suspending the audio graph directly, without ever calling into setActionHandler callbacks.
previoustrack, nexttrack, and seek commands aren't implemented cases at all, they are no-ops. This is a gap in WebKit itself, not something fixable through a script. Contrast that with MediaElementSession::didReceiveRemoteControlCommand(), which does dispatch every remote command through to setActionHandler, a real HTMLMediaElement is the only path that gets you this.