Обработка событий воспроизведения в CarPlay с помощью MPNowPlayingInfoCenter

Я пытаюсь создать пример аудио-приложения с интеграцией CarPlay. Приложение представляет собой тестовый проект - без API, без потоковой передачи, без сложного пользовательского интерфейса. Просто краткий список названий песен с функцией выбора и воспроизведения. Моя цель - обработать обратный вызов при нажатии кнопки воспроизведения на экране "Сейчас исполняется" и воспроизвести файл.

У меня нет проблем с настройкой MPPlayableContentManager, MPPlayableContentDataSource и MPPlayableContentDelegate. Мой контент анализируется из файла JSON, и он правильно отображается на экране симулятора.

// MARK: - MPPlayableContentDataSource methods
extension PlayableContentManager: MPPlayableContentDataSource {
  func numberOfChildItems(at indexPath: IndexPath) -> Int {
    if indexPath.count == 0 {
      return playlists.count
    } else if indexPath.count == 1 {
      return playlists[indexPath.first ?? 0].playlist.count
    } else if indexPath.count == 2 {
      return playlists.last?.playlist.count ?? 0
    }

    return 0
 }

  func contentItem(at indexPath: IndexPath) -> MPContentItem? {
    if indexPath.count == 1 {
      return createTabbar(item: playlists[indexPath.first ?? 
0].name.capitalized, itemType: playlists[indexPath.first ?? 0].type)
    } else if indexPath.count == 2 {
      if indexPath.first == 0 {
        return createContent(item: playlists[0].playlist[indexPath.last 
?? 0], isContainer: false, isPlayable: true)
      } else {
        return createContainerContent(item: playlists[indexPath.first 
?? 0].playlist[indexPath.last ?? 0])
      }
    }

    return createTabbar(item: "", itemType: nil)
  }
}

Этот код дает следующий графический вывод:

CarPlay симулятор

Две вкладки содержат два списка воспроизведения. В каждом плейлисте есть несколько песен.

Когда я нажимаю на песню, приложение становится приложением "Сейчас исполняется", но только в том случае, если я одновременно начал и закончил получать события дистанционного управления во время взаимодействия пользователя со строкой.

Метод initiatePlaybackOfContentItemAt вызывается при обнаружении щелчка по строке таблицы.

// MARK: - MPPlayableContentDelegate methods
extension PlayableContentManager: MPPlayableContentDelegate {
  func playableContentManager(_ contentManager: 
MPPlayableContentManager, initiatePlaybackOfContentItemAt indexPath: 
 IndexPath, completionHandler: @escaping (Error?) -> Void) {
    DispatchQueue.main.async {
      UIApplication.shared.beginReceivingRemoteControlEvents()
      self.infoCenter.nowPlayingInfo = self.setupNowPlayingInfo(for: 
indexPath)
      completionHandler(nil)
      UIApplication.shared.endReceivingRemoteControlEvents()
    }
  }
}

Это единственный код, который работает для меня, если я хочу, чтобы приложение перешло на экран "Сейчас исполняется". Если я размещу какой-либо из методов UIApplication где-либо еще, приложение перестанет реагировать на прикосновения строк и не откроет экран "Сейчас исполняется".

Тем не менее, я думаю, потому что я вызываю endReceivingRemoteControlEvents (), я не могу получить обратный вызов для различных событий. Теперь информация о воспроизведении установлена, я вижу кнопку воспроизведения в пользовательском интерфейсе, но когда я нажимаю ее, обратный вызов не выполняется.

private func setupPlaybackCommands() {
    commandCenter = MPRemoteCommandCenter.shared()

    commandCenter.playCommand.addTarget { [unowned self] event in
      if self.audioPlayer.rate == 0.0 {
        self.play()
        return .success
      }
      return .commandFailed
    }
....
}

Сейчас играет экран

Что я делаю неправильно?

Может ли это быть как-то связано с тем, что я тестирую на симуляторе? Будет ли это работать на реальном устройстве?

Если кто-то может пролить свет на то, как правильно настроить интеграцию CarPlay, чтобы перейти на экран "Сейчас исполняется" и ответить на события, поделитесь. У меня много проблем с поиском каких-либо полезных примеров кода или примеров.

Я знаю, что могу, но я не подал заявку на получение разрешения CarPlay, потому что этот проект предназначен только для исследовательских целей, и я очень сомневаюсь, что получу одобрение.

1 ответ

Я был в состоянии "исправить" playCommand а также stopCommand получать обратные вызовы, только если вызов beginReceivingRemoteControlEvents метод после быстрой паузы (вроде секунды):

extension PlayableContentManager: MPPlayableContentDelegate {
  func playableContentManager(_ contentManager: 
MPPlayableContentManager, initiatePlaybackOfContentItemAt indexPath: 
 IndexPath, completionHandler: @escaping (Error?) -> Void) {
    DispatchQueue.main.async {
      UIApplication.shared.beginReceivingRemoteControlEvents()
      self.infoCenter.nowPlayingInfo = self.setupNowPlayingInfo(for: 
indexPath)
      completionHandler(nil)
      UIApplication.shared.endReceivingRemoteControlEvents()

      // TODO: add 1 second timeout, and call this after
      UIApplication.shared.beginReceivingRemoteControlEvents()
    }
  }
}

Еще одна вещь, когда пользователь переходит на экран "Сейчас играет", iOS предполагает, что пользователь должен нажать кнопку "Воспроизвести", и только после этого вы должны начать воспроизведение (обработав обратный вызов удаленной команды). В противном случае вы будете перенаправлены на экран "Сейчас исполняется" с активным звуком и без возможности сделать кнопку со значком "Стоп".

Другие вопросы по тегам