MPNowPlayingInfo only displaying after changing playback state (SwiftUI + AVAudioPlayer)

114 views Asked by At

I have successfully setup a background capable audio player with Lock Screen controls, but when it comes to setting the info, nothing seems to work. When I first start playing music in the app and move to the background/lockscreen/control center, you can only see the controls (no info). But when I hit play or pause, the info then appears for a few seconds before disappearing. It is really quite strange behavior.

Info shown after initially opening the Lock Screen:

Lock Screen

Info shown after pausing the player:

Lock Screen after tapping pause

Background modes:

Audio, AirPlay, and Picture in Picture

App Delegate:

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()

        do {
            try AVAudioSession.sharedInstance().setCategory(.playback)
            try AVAudioSession.sharedInstance().setActive(true)
        } catch {
            print("Error configuring audio session: \(error)")
        }

        return true
    }
}

As you can see, I am setting the category and setting the session to active. Now for the main bit of code - my MusicPlayerManager(), which has 3 important functions: create an audio player, setup the MPRemoteCommandCenter (controls), and the MPNowPlayingInfoCenter (info). In my main view's onAppear I am calling setPlayer(withURL:)

Music Player Manager:

class MusicPlayerManager: ObservableObject {
    @Published var audioPlayer: AVAudioPlayer? = nil
    @Published var isPlaying: Bool = false {
        didSet {
            if isPlaying {
                audioPlayer?.play()
            } else {
                audioPlayer?.pause()
            }

            setupNowPlayingInfo() /// update info (WORKS)
        }
    }

    /// create avaudioplayer
    func setPlayer(withURL: URL) {
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: withURL)
            isPlaying = true

            setupRemoteControlCenter()
            setupNowPlayingInfo() /// THIS DOESN'T WORK
            print(MPNowPlayingInfoCenter.default().nowPlayingInfo) /// CORRECTLY PRINTS INFO
        } catch {
            print("Failed to set audio player: \(error)")
        }
    }

    /// setup controls
    private func setupRemoteControlCenter() {
        let commandCenter = MPRemoteCommandCenter.shared()

        commandCenter.playCommand.addTarget { event in
            self.isPlaying = true
            return .success
        }

        commandCenter.pauseCommand.addTarget { event in
            self.isPlaying = false
            return .success
        }
    }

    /// set info
    private func setupNowPlayingInfo() {
        var nowPlayingInfo: [String: Any] = [:]

        nowPlayingInfo[MPMediaItemPropertyTitle] = "Your Track Title"
        nowPlayingInfo[MPMediaItemPropertyArtist] = "Your Artist"

        if let albumArtwork = UIImage(named: "app-icon") {
            nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: albumArtwork.size) { _ in albumArtwork }
        }

        nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = true

        MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
    }
}

For some reason, the initial call to setupNowPlayingInfo() from setPlayer(withURL:) doesn't actually work, I can however successfully print the info. But the info is not displayed. Ironically, when you hit play/pause from the Lock Screen, the info suddenly appears for a few seconds. Pausing/playing the audio in app has no effect. (I am using MPNowPlayingInfoPropertyIsLiveStream, but setting the duration, current playback, etc. doesn't have any effect)

I am really confused on why my now playing info is not properly displaying, am I forgetting something here?

Tested with Xcode 15.0.1 & 15.1

Tested on an iPhones 14 Pro Max (iOS 17.1.2) & iPhone 11 Pro Max (iOS 16.6.1)

EDIT: I am now getting this log when I try to set the player info for the first time:

Entitlement: com.apple.mediaremote.external-artwork-validation - Entitled: NO - Error: (null)

Even if my artwork fails to load, shouldn't my other info still display?

0

There are 0 answers