为什么 iOS 13.2 中的 MPPlayableContentManager 协议(MPPlayableContentDataSource 和 MPPlayableContentDelegate)没有被调用?

Why MPPlayableContentManager protocols (MPPlayableContentDataSource and MPPlayableContentDelegate) in iOS 13.2 is not called?

Carplay 音频应用在 iOS v12.4 之前一直有效。当我创建下面的 class 时,我遵循了这个 的答案并且它工作得很好。下面是示例代码。

Carplay 正在播放,但在仪表盘上打开应用程序时,显示无法连接。我正在使用 Xcode v11.2.1 (11B500),模拟器 iOS v13.2.2,iPhone 11 (v13.2.3).

曾尝试在 initiatePlaybackOfContentItemAt 上打印日志,但没有被调用。您认为我缺少什么,以便它可以工作并与 iOS 13 或更高版本兼容?

class PlayManager: NSObject {

    static let shared = PlayManager()
    var currentStation: Int64 = 0
    var isRadio: Bool = true
    var contentList: [RadioObj] = []
    let commandCenter = MPRemoteCommandCenter.shared()
    var nowPlayingSongManager: MPNowPlayingInfoCenter?
    var playableContentManager: MPPlayableContentManager?

    override init() {
        super.init()

        let artist = "Play"
        nowPlayingSongManager = MPNowPlayingInfoCenter.default()
        nowPlayingSongManager?.nowPlayingInfo = [
            MPNowPlayingInfoPropertyMediaType : "Audio",
            MPMediaItemPropertyTitle : api.defaults.defaultArtist,
            MPMediaItemPropertyArtist: artist
        ]
        player.initializeMedia(song: api.defaults.defaultArtist, artist: artist, album: api.defaults.appLogo)

        playableContentManager = MPPlayableContentManager.shared()
        playableContentManager?.dataSource = self
        playableContentManager?.delegate = self
    }

    func loadData(){
        api.data.getRadioData(event_source: "carplay") { (done, obj) in

            if done {
                if obj.count > 0 {
                    let indeces: Int = 0
                    api.currentRadio = obj[indeces]
                    api.isRadio = true
                    api.isPodcast = false
                }

                self.contentList = obj
                self.playableContentManager.reloadData()
            }
        }
    }
}


extension PlayManager: MPPlayableContentDelegate, MPPlayableContentDataSource {

    func playableContentManager(_ contentManager: MPPlayableContentManager, initiatePlaybackOfContentItemAt indexPath: IndexPath, completionHandler: @escaping (Error?) -> Void) {

        print("check if its called")

        let indeces: Int = indexPath[0]
        let radio_name = contentList[indeces].name
        let id = contentList[indeces].id
        let radio_logo = contentList[indeces].logo_rounded_url
        let stream_url = contentList[indeces].stream_url
        let hd_stream_url = contentList[indeces].hd_stream_url
        var currentIdentifier = ""

        if contentManager.nowPlayingIdentifiers.count > 0 {
            currentIdentifier = contentManager.nowPlayingIdentifiers[0]
        }

        var newIdenditier = "radio_"

        if let id = id {
            newIdenditier += "\(id)"
        }

        let radioObj = RadioObj()
        radioObj.stream_url = stream_url
        radioObj.hd_stream_url = hd_stream_url
        radioObj.name = radio_name
        radioObj.logo_url = radio_logo

        api.currentRadio = contentList[indeces]
        api.isRadio = true

        if let id = id {
            currentStation = id
        }

        guard let logo = radio_logo, let name = radio_name else { return }

        player.initializeMedia(song: name, artist: api.defaults.defaultArtist, album: api.defaults.appLogo)

        api.download(image: logo) { (image) in
            player.initializeMedia(song: name, artist: api.defaults.defaultArtist, album: image)
        }

        if api.isPlaying && currentIdentifier != newIdenditier {
            contentManager.nowPlayingIdentifiers = [newIdenditier]
            DispatchQueue.main.async {
                player.start()
            }
        }

        onTapSound(contentManager: contentManager, completionHandler: completionHandler, indexPath: indexPath)
    }

    func onTapSound(contentManager:MPPlayableContentManager,completionHandler: @escaping (Error?) -> Void, indexPath : IndexPath){
        completionHandler(nil)
    }

    func numberOfChildItems(at indexPath: IndexPath) -> Int {
        if indexPath.count == 0 {
            return contentList.count
        } else if indexPath.count == 1 {
            let _: Int = indexPath.first ?? 0

        } else if indexPath.count == 2 {

        }
        return 0
    }


    func contentItem(at indexPath: IndexPath) -> MPContentItem? {
        let indeces: Int = indexPath[0]
        let _: Int = indexPath.indices.count

        let radio_name = contentList[indeces].name
        let id = contentList[indeces].id
        let radio_logo = contentList[indeces].car_logo
        if let id = id, let radio_logo = radio_logo {
            let contentItem = MPContentItem(identifier: "radio_\(id)")
            contentItem.title = radio_name
            contentItem.isContainer = false
            contentItem.isPlayable = true
            contentItem.isStreamingContent = true

            DispatchQueue.global().async {
                if let url = URL(string:radio_logo) {
                    do {
                        let radio =  try UIImage(data: Data(contentsOf: url))
                        if let image = radio {

                            let artWork = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { (size) -> UIImage in
                                return image
                            })

                            contentItem.artwork = artWork
                        }
                    } catch{}
                }
            }

            return contentItem
        }

        return MPContentItem()
    }

    open func setupEvents(isRadio isTrue: Bool?) -> PlayManager {
        guard let isTrue = isTrue else { return self }
        commandCenter.playCommand.isEnabled = isTrue


        if isTrue {
            commandCenter.skipForwardCommand.isEnabled = false
            commandCenter.skipBackwardCommand.isEnabled = false
            commandCenter.stopCommand.isEnabled = true
            commandCenter.pauseCommand.isEnabled = false

            if #available(iOS 10.0, *) {
                nowPlayingSongManager?.nowPlayingInfo?[MPNowPlayingInfoPropertyIsLiveStream] = true
            }
        } else {
            commandCenter.stopCommand.isEnabled = false
            commandCenter.pauseCommand.isEnabled = true
        }

        commandCenter.stopCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
            api.isPlaying = false
            player.stop()
            return .success
        }

        commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
            player.pause()
            return .success
        }

        commandCenter.playCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
            api.isPlaying = true

            if api.isRadio {
                self.playableContentManager?.nowPlayingIdentifiers = ["radio_\(self.currentStation)"]

                guard let logo = api.currentRadio?.logo_rounded_url, let name = api.currentRadio?.name else { return .noSuchContent }

                api.download(image: logo) { (image) in
                    player.initializeMedia(song: name, artist: api.defaults.defaultArtist, album: image)
                }

                player.start()
            }

            if api.isPodcast {
                player.resume()
            }

            return .success
        }

        return self
    }
}

似乎是 Xcode 11 错误。我试过 Xcode 13 beta,但结果是一样的。

在我的例子中,有一个空白的白色屏幕,带有蓝色调的空白标签。然后显示错误,说 "Unable to load content (null)" iOS 13 Simulator screenshot.

完成安装 iPhone 5s/iOS 12.4.1 模拟器。

在客户端使用真实设备iOS 13 按预期工作。

我前段时间为此向 Apple 提交了错误报告。 Apple 回复我说:

"We need a sysdiagnose and, ideally, some sample code reproducing the issue please.

beginLoadingChildItems() is just a hint to the app to start prefetching content, in the event that the user taps a row. Instead, it seems more likely that the client app is not providing any content to MPPlayableContentManager."

你可以看到这个问题之前在 Apple Developer forums.

上被问过

CarPlay 模拟器在 Xcode 11 和 iOS 12.4 模拟器上运行良好,但在 Xcode 11 和 iOS 13 模拟器上似乎存在问题。我尝试在 Xcode 11 和 运行 上构建我们的应用程序,并在物理 iOS 13.1 设备上构建物理车载 CarPlay 多媒体系统,它运行完美!这似乎只是 CarPlay 模拟器的问题。