ingageco/capacitor-music-controls-plugin

Media controls not showing iOS

Christilut opened this issue · 17 comments

I have the media controls working on Android, but they don't appear on iOS.

This is my code:

import { CapacitorMusicControls } from 'capacitor-music-controls-plugin-v3'

export function mediaControlsShow(track: BaseTrack) {
  CapacitorMusicControls.create({
    track: track.title,
    artist: track.artist,
    album: track.albumTitle,
    cover: '', // TODO: 
    // cover: 'albums/absolution.jpg', // optional, default : nothing
    // cover can be a local path (use fullpath 'file:///storage/emulated/...', or only 'my_image.jpg' if my_image.jpg is in the www folder of your app)
    // or a remote url ('http://...', 'https://...', 'ftp://...')

    // hide previous/next/close buttons:
    hasPrev: true, // show previous button, optional, default: true
    hasNext: true, // show next button, optional, default: true
    hasClose: true, // show close button, optional, default: false

    // iOS only, optional
    duration: track.duration, // optional, default: 0
    elapsed: 0, // optional, default: 0
    // hasSkipForward: false, // optional, default: false. true value overrides hasNext.
    // hasSkipBackward: false, // optional, default: false. true value overrides hasPrev.
    // skipForwardInterval: 15, // optional. default: 15.
    // skipBackwardInterval: 15, // optional. default: 15.
    hasScrubbing: false, // optional. default to false. Enable scrubbing from control center progress bar

    // Android only, optional
    isPlaying: true, // optional, default : true
    dismissable: true, // optional, default : false
    // text displayed in the status bar when the notification (and the ticker) are updated
    ticker: `Now playing "${track.title}"`,
    // All icons default to their built-in android equivalents
    // The supplied drawable name, e.g. 'media_play', is the name of a drawable found under android/res/drawable* folders
    playIcon: 'media_play',
    pauseIcon: 'media_pause',
    prevIcon: 'media_prev',
    nextIcon: 'media_next',
    closeIcon: 'media_close',
    notificationIcon: 'notification'
  }).then(() => {
    // SUCCESS
    // console.log('showing media notification')
  })
    .catch(e => {
      console.log(e)
    })
}

This is in the xcode logs, not sure if that is related:

2023-03-15 15:58:34.684018+0100 App[801:31337] [assertion] Error acquiring assertion: <Error Domain=RBSServiceErrorDomain Code=1 "(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)" UserInfo={NSLocalizedFailureReason=(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)}>
2023-03-15 15:58:34.684227+0100 App[801:31337] [ProcessSuspension] 0x11006c130 - ProcessAssertion: Failed to acquire RBS assertion 'WebKit Media Playback' for process with PID=804, error: Error Domain=RBSServiceErrorDomain Code=1 "(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)" UserInfo={NSLocalizedFailureReason=(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)}
2023-03-15 15:58:34.686862+0100 App[801:31337] [assertion] Error acquiring assertion: <Error Domain=RBSServiceErrorDomain Code=1 "(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)" UserInfo={NSLocalizedFailureReason=(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)}>
2023-03-15 15:58:34.687087+0100 App[801:31337] [ProcessSuspension] 0x11006c180 - ProcessAssertion: Failed to acquire RBS assertion 'WebKit Media Playback' for process with PID=801, error: Error Domain=RBSServiceErrorDomain Code=1 "(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)" UserInfo={NSLocalizedFailureReason=(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)}
2023-03-15 15:58:34.689031+0100 App[801:31337] [assertion] Error acquiring assertion: <Error Domain=RBSServiceErrorDomain Code=1 "(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)" UserInfo={NSLocalizedFailureReason=(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)}>
2023-03-15 15:58:34.689351+0100 App[801:31337] [ProcessSuspension] 0x11006c1d0 - ProcessAssertion: Failed to acquire RBS assertion 'WebKit Media Playback' for process with PID=805, error: Error Domain=RBSServiceErrorDomain Code=1 "(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)" UserInfo={NSLocalizedFailureReason=(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)}
⚡️  To Native ->  CapacitorMusicControls updateIsPlaying 22420832

I have Audio in background modes enabled.

Is there an entitlement or something else that is required?

Can you please confirm that you're using the setup / code from this repo and not v2?

Using v3 yes

    "@capacitor-community/keep-awake": "^3.0.0",
    "@capacitor-community/sqlite": "4.6.0",
    "@capacitor/android": "4.6.1",
    "@capacitor/app": "4.1.1",
    "@capacitor/core": "4.6.1",
    "@capacitor/filesystem": "^4.1.4",
    "@capacitor/haptics": "4.1.0",
    "@capacitor/ios": "4.6.1",
    "@capacitor/keyboard": "4.1.0",
    "@capacitor/status-bar": "4.1.1",

    "capacitor-music-controls-plugin-v3": "^1.0.10",

Please attempt with the setup code in the Readme - don't comment out any of the fields you aren't using. Specifically the create method. Of course, customize with your track info but keep all ios parameters.

Same thing, used this code:


export function mediaControlsShow(track: BaseTrack) {
  CapacitorMusicControls.create({
    track: track.title,
    artist: track.artist,
    album: track.albumTitle,
    cover: '', // TODO: 
    // cover: 'albums/absolution.jpg', // optional, default : nothing
    // cover can be a local path (use fullpath 'file:///storage/emulated/...', or only 'my_image.jpg' if my_image.jpg is in the www folder of your app)
    // or a remote url ('http://...', 'https://...', 'ftp://...')

    // hide previous/next/close buttons:
    hasPrev: true, // show previous button, optional, default: true
    hasNext: true, // show next button, optional, default: true
    hasClose: true, // show close button, optional, default: false

    // iOS only, optional
    duration: track.duration, // optional, default: 0
    elapsed: 0, // optional, default: 0
    hasSkipForward: false, // optional, default: false. true value overrides hasNext.
    hasSkipBackward: false, // optional, default: false. true value overrides hasPrev.
    skipForwardInterval: 15, // optional. default: 15.
    skipBackwardInterval: 15, // optional. default: 15.
    hasScrubbing: false, // optional. default to false. Enable scrubbing from control center progress bar

    // Android only, optional
    isPlaying: true, // optional, default : true
    dismissable: true, // optional, default : false
    // text displayed in the status bar when the notification (and the ticker) are updated
    ticker: `Now playing "${track.title}"`,
    // All icons default to their built-in android equivalents
    // The supplied drawable name, e.g. 'media_play', is the name of a drawable found under android/res/drawable* folders
    playIcon: 'media_play',
    pauseIcon: 'media_pause',
    prevIcon: 'media_prev',
    nextIcon: 'media_next',
    closeIcon: 'media_close',
    notificationIcon: 'notification'
  }).then(() => {
    // SUCCESS
    // console.log('showing media notification')
  })
    .catch(e => {
      console.log(e)
    })
}

It does show a success message in the then() function.

Anything I can do to debug this?

Could this be related to WebAudio? I'm using that to play sound.

Sound stops when I minimize the app, even with the Audio background mode enabled. So maybe iOS isn't picking up the audio when it comes from WebAudio?

I would think that is unrelated because I'm just sending a signal to the media controls and my app listens to events, but maybe this is an iOS quirk.

I went into the swift code and set up the most basic thing possible:

      MPNowPlayingInfoCenter.default().nowPlayingInfo = [
          MPMediaItemPropertyArtist: "Foo",
          MPMediaItemPropertyTitle: "Bar"
        ]

        call.resolve()
        return

But that didn't work either. The code does get called.

Must be something unrelated to the plugin? Keeps coming back to the assertion error in the logs, in my first post

@ingageco @Christilut
I started porting my existing Android project to iOS and I am running in the same issues as mentioned by @Christilut. The media controls are not showing up on iOS (tested with emulator).

When only creating the iOS Media Controls with the required parameters, the Simulator logs show following output:

⚡️  To Native ->  CapacitorMusicControls create 120241803
MusicControlsOptions:
(key: AnyHashable("hasSkipForward"), value: 0)
(key: AnyHashable("skipForwardInterval"), value: 15)
(key: AnyHashable("hasSkipBackward"), value: 0)
(key: AnyHashable("hasScrubbing"), value: 0)
(key: AnyHashable("hasClose"), value: 1)
(key: AnyHashable("track"), value: "Time is Running Out")
(key: AnyHashable("cover"), value: "https://play-lh.googleusercontent.com/coMv1dl31PCfEs6essJoEUwVryaqKHKQvENdZ_WYpN-PXa8Qfitkg3grQxIVN22W5A=w480-h960")
(key: AnyHashable("skipBackwardInterval"), value: 15)
(key: AnyHashable("artist"), value: "Muse")
(key: AnyHashable("hasPrev"), value: 1)
(key: AnyHashable("album"), value: "Absolution")
(key: AnyHashable("hasNext"), value: 1)
(key: AnyHashable("elapsed"), value: 10)
(key: AnyHashable("duration"), value: 60)
⚡️  TO JS undefined
⚡️  [log] - Media controls created successfully.
2023-03-21 13:06:20.562638+0200 App[9091:91188] [MediaRemote] WARNING: Unexpected type for now playing key kMRMediaRemoteNowPlayingInfoPlaybackRate (is CFNull, should be CFNumber). Removing from now playing info dictionary.

The promise reports successful creation of the controls, but nothing is shown. There is also a warning printed regarding unexpected type, but that shouldn't prevent the controls creation.

@Christilut Sorry my friend - Github didn't notify me of your response. I have not tested this with web audio at all, but i have recently launched an app on both iOS and Android with everything working as expected (using current version on NPM). I'm using Cordova Media to play the audio (found it much more reliable than web audio in testing, but that was 6+ years ago)

I recently changed something regarding the nowPlaying key. That seems be the focus of the error @NorthFred posted - just not sure why it didn't show up in my testing. Let me investigate and i'll get back with you

@NorthFred @Christilut Just FYI, emulator does not show media controls. You have to test on a real device. Seems silly that this can't be implemented in the emulator.

@ingageco Thanks for looking into this. I'm testing everything on a real device, latest iOS

@ingageco Any news on this? I've tried with the latest release (1.0.16) but same problem.

I can help debug it, just not sure where to look right now.

I'm still not sure where there's an issue here. I'm testing with 1.0.17 and everything works as expected. Latest iOS.

I ran more tests on an empty project and it seems that iOS requires you to have an <audio> element playing before you can use the media controls. Since I use WebAudio, there is no audio element and the media controls simply don't show.

So for now, my workaround will be to create an audio element with only silence that is playing when my WebAudio is playing and is paused when WebAudio is paused. Hacky but should work.

@Christilut Yes, i do believe you must have audio playing on the device before you have the opportunity to create controls. So this isn't an issue with the plugin - just make sure the audio starts before the controls are created.

Yeah but I use WebAudio so audio was always playing when trying this, but it specifically needs an <audio> element or new Audio() playing.