首页 > 解决方案 > 为什么不调用 iOS 13.2 中的 MPPlayableContentManager 协议(MPPlayableContentDataSource 和 MPPlayableContentDelegate)?

问题描述

Carplay 音频应用程序一直运行到 iOS v12.4。当我创建下面的类时,我按照这个链接上的答案,它工作得很好。下面是示例代码。

正在播放的 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
    }
}

标签: swiftxcodeswift3ios13carplay

解决方案


不久前,我向 Apple 提交了一份错误报告。苹果回复我说:

“我们需要系统诊断,理想情况下,请提供一些重现问题的示例代码。

beginLoadingChildItems() 只是提示应用程序在用户点击一行时开始预取内容。相反,客户端应用程序似乎更有可能没有向 MPPlayableContentManager 提供任何内容。”

你可以看到这个问题之前在苹果开发者论坛上被问过。

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


推荐阅读