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:
Info shown after pausing the player:
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?