swiftavplayeravaudiosessionmpnowplayinginfocenter

iOS MPNowPlayingInfoCenter not Displaying Using AVPlayer and AVAudioSession


I am building a radio streaming app. The sound is working, including when the app is in the background and when the phone is locked. However, I can't get the MediaPlayer info to appear on the lock screen.

I am attempting to use a combination of AVPlayer, AVAudioSession, and MPNowPlayingInfoCenter. I have looked at other questions on Stack Overflow (this one, especially), but it seems like their solutions aren't working for me.

I will try to lay out the important aspects of my app:

Info.plist

Added Required background modes: App plays audio or streams audio/video using AirPlay

AppDelegate.swift

I added the following code to didFinishLaunchingWithOptions

do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
            print("Playback OK")
            try AVAudioSession.sharedInstance().setActive(true)
            print("Session is Active")
        } catch {
            print(error)
        }

and then I overrode remoteControlReceived

override func remoteControlReceived(with event: UIEvent?) {
        let rc = event!.subtype
                print("does this work? \(rc.rawValue)")
    }

Streaming Model

In a struct, I created a radio model using AVPlayer. The stream is successfully sent to the ViewController.

ViewController

In viewDidLoad, the following code is run

override func viewDidLoad() {
        super.viewDidLoad()
        self.becomeFirstResponder()
        UIApplication.shared.beginReceivingRemoteControlEvents()
}

I also overrode the following two functions in the ViewController, following another tutorial:

override var canBecomeFirstResponder: Bool {
        return true;
    }
    
    override func remoteControlReceived(with event: UIEvent?) {
        let type = event?.subtype
        
        if (type == UIEvent.EventSubtype.remoteControlTogglePlayPause) {
            //if ([self.radioManager.player.])
            
        }
        
    }

Lastly, I wrote the following function, that I've been adding to as I read new tutorials. I run this function after the stream has started playing:

func setNowPlayingInfo() {

        
        UIApplication.shared.beginReceivingRemoteControlEvents()
        MPRemoteCommandCenter.shared()
        MPRemoteCommandCenter.shared().playCommand.addTarget {event in
              return .success
            }
            MPRemoteCommandCenter.shared().pauseCommand.addTarget {event in
              return .success
            }
            MPRemoteCommandCenter.shared().nextTrackCommand.addTarget {event in
              return .success
            }
            MPRemoteCommandCenter.shared().previousTrackCommand.addTarget {event in
              return .success
            }
        
        var nowPlayingInfo = [String: Any]()
        // prepare title and subtitle
        nowPlayingInfo[MPMediaItemPropertyTitle] = "Test"
    
        MPNowPlayingInfoCenter.default().nowPlayingInfo = [
            MPMediaItemPropertyTitle: self.trackTitle.text,
            MPMediaItemPropertyArtist: self.nameLabel.text
        
        ]
    }

One weird thing is that when I have added the line MPNowPlayingInfoCenter.default().playbackState = .playing, the MediaPlayer is shown. When I've tested it out on an actual phone, though (compared to the simulator), the app stops, because that property is only for MacOS.

When the app is streaming, and I try opening Youtube, the YouTube video starts on mute. That makes me think the phone recognizes my app's AVAudioSession. Additionally, when I call print(MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPMediaItemPropertyTitle]) after running setNowPlayingInfo(), the correct info is printed.

Any suggestions are greatly appreciated. I apologize for the really long question.

Edit

Maybe this issue is caused by my AVPlayer not being initialized properly? Here is the initialization code in a .swift file that communicates with the View Controller through a delegate

struct RadioManager {
    var player: AVPlayer = AVPlayer.init()
    var streamPlaying = false
    var badRadioURL = "http://server.badradio.biz:8000/"
    var delegate: RadioManagerDelegate?
    
    mutating func initializeRadio() {
        let urlString = "\(badRadioURL)stream"
        guard let url = URL.init(string: urlString)
                else {
                    return
            }
        let playerItem = AVPlayerItem.init(url: url)
        player = AVPlayer.init(playerItem: playerItem)
    }
    
    mutating func stream() {
        print("loading")
    
        player.play()
        streamPlaying = true
    
    }

Solution

  • Okay, I solved it. My app was fine; the issue was that the simulator wasn't acting like an actual device. When I tested it on an actual iPad, the lock screen info displayed perfectly. It still doesn't display on the simulator, though. Thank you for your time.