I'm playing audio from a JSON source that I built to feed the audio URL and it's audio information to my app (Such as title, description, and cover art URL). The audio is a radio station feed. Audio works, play/stop controls work, cover art works, and background audio works. I'm stumped on adding the controls to Control Center to play/stop the audio while outside the app. I've taken a look at Apple's Documentation on this and it's very straight forward. I've also enabled the build setting to allow for background fetch, background audio, and Bluetooth. But it doesn't seem to work on my iPhone attached to Xcode via USB-C (I'm assuming iOS Simulator doesn't support Control Center). Below is my working code. Any thoughts on how to get this working? Do I need to pass on the audio to setupRemoteTransportControls()
? I'm assuming though Title and Cover Art will need to be passed from data source, but overall the Control Center will not recognize that the app is playing audio.
ViewController.swift
import UIKit
import MediaPlayer
import AVFoundation
import Foundation
class ViewController: UIViewController {
var player: AVPlayer!
var playerItem: AVPlayerItem!
var audioCheck: Timer?
var timer: Timer?
var passText: String? = "Test"
@IBOutlet weak var trackTitle: UILabel!
@IBOutlet weak var togglePlay: UIButton!
@IBOutlet weak var coverPhoto: UIImageView!
func loadAudio() {
let audioURL = URL.init(string: "AUDIO_URL_GOES_HERE")
player = AVPlayer.init(url: audioURL!)
}
let minutes = 60
func playAudio() {
let audioURL = URL.init(string: "AUDIO_URL_GOES_HERE")
player = AVPlayer.init(url: audioURL!)
player.play()
}
@IBAction func OpenPlayer(_ sender: Any) {
performSegue(withIdentifier: "PlayerSegue", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
loadAudio()
// Audio in the background
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSession.Category.playback)
} catch {
print("Error: Audio is not paying in the background.")
}
// Timer checking if other audio sources are running
audioCheck = Timer.scheduledTimer(timeInterval: 0, target: self, selector: #selector(timedAudioCheck), userInfo: nil, repeats: true)
struct currentTrack: Codable {
let title: String
let artwork_url: String
}
struct getTrack: Codable {
// let name: String
// let status: String
let current_track: currentTrack
}
let jsonURL = URL(string: "JSON_URL_GOES_HERE")!
URLSession.shared.dataTask(with: jsonURL) {data, _, _ in
if let data = data {
let trackData = try? JSONDecoder().decode([getTrack].self, from: data)
// print(users)
// print(trackData![0].current_track.title)
// print(trackData![0].current_track.artwork_url)
DispatchQueue.main.async {
self.trackTitle.text = trackData![0].current_track.title
let coverPhotoURL = trackData![0].current_track.artwork_url
if let coverPhotoConverted = URL(string: coverPhotoURL) {
do {
let coverPhotoData = try Data(contentsOf: coverPhotoConverted)
self.coverPhoto.image = UIImage(data: coverPhotoData)
} catch {
}
}
}
}
}.resume()
_ = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true) { timer in
// print("Data updated!")
let jsonURL = URL(string: "JSON_URL_GOES_HERE")!
URLSession.shared.dataTask(with: jsonURL) {data, _, _ in
if let data = data {
let trackData = try? JSONDecoder().decode([getTrack].self, from: data)
// print(users)
// print(trackData![0].current_track.title)
// print(trackData![0].current_track.artwork_url)
DispatchQueue.main.async {
self.trackTitle.text = trackData![0].current_track.title
let coverPhotoURL = trackData![0].current_track.artwork_url
if let coverPhotoConverted = URL(string: coverPhotoURL) {
do {
let coverPhotoData = try Data(contentsOf: coverPhotoConverted)
self.coverPhoto.image = UIImage(data: coverPhotoData)
} catch {
}
}
}
}
}.resume()
}
func setupRemoteTransportControls() {
// Get the shared MPRemoteCommandCenter
let commandCenter = MPRemoteCommandCenter.shared()
// Add handler for Play Command
commandCenter.playCommand.addTarget { [unowned self] event in
if self.player.rate == 0.0 {
self.player.play()
return .success
}
return .commandFailed
}
// Add handler for Pause Command
commandCenter.pauseCommand.addTarget { [unowned self] event in
if self.player.rate == 1.0 {
self.player.pause()
return .success
}
return .commandFailed
}
}
// Apple's Documentation on playing audio in control center
func setupRemoteTransportControls() {
// Get the shared MPRemoteCommandCenter
let commandCenter = MPRemoteCommandCenter.shared()
// Add handler for Play Command
commandCenter.playCommand.addTarget { [unowned self] event in
if self.player.rate == 0.0 {
self.player.play()
return .success
}
return .commandFailed
}
// Add handler for Pause Command
commandCenter.pauseCommand.addTarget { [unowned self] event in
if self.player.rate == 1.0 {
self.player.pause()
return .success
}
return .commandFailed
}
}
func setupNowPlaying() {
// Define Now Playing Info
var nowPlayingInfo = [String : Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = "My Movie"
if let image = UIImage(named: "lockscreen") {
nowPlayingInfo[MPMediaItemPropertyArtwork] =
MPMediaItemArtwork(boundsSize: image.size) { size in
return image
}
}
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = playerItem.currentTime().seconds
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = playerItem.asset.duration.seconds
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate
// Set the metadata
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
}
// Along with timer, checks if other audio sources are playing and resets the audio in the app
@objc func timedAudioCheck() {
if (AVAudioSession.sharedInstance().secondaryAudioShouldBeSilencedHint) {
togglePlay.setTitle("Play", for: .normal)
} else if (player.rate == 0) {
togglePlay.setTitle("Play", for: .normal)
} else {
togglePlay.setTitle("Puase", for: .normal)
}
}
@IBAction func togglePlay(_ sender: UIButton) {
if player.rate == 0 {
// Plays the audio stream
sender.setTitle("Pause", for: .normal)
playAudio()
} else {
sender.setTitle("Play", for: .normal)
player.pause()
}
}
}
Apple's suggestion to add Control Center Support
func setupRemoteTransportControls() {
// Get the shared MPRemoteCommandCenter
let commandCenter = MPRemoteCommandCenter.shared()
// Add handler for Play Command
commandCenter.playCommand.addTarget { [unowned self] event in
if self.player.rate == 0.0 {
self.player.play()
return .success
}
return .commandFailed
}
// Add handler for Pause Command
commandCenter.pauseCommand.addTarget { [unowned self] event in
if self.player.rate == 1.0 {
self.player.pause()
return .success
}
return .commandFailed
}
}
func setupNowPlaying() {
// Define Now Playing Info
var nowPlayingInfo = [String : Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = "My Movie"
if let image = UIImage(named: "lockscreen") {
nowPlayingInfo[MPMediaItemPropertyArtwork] =
MPMediaItemArtwork(boundsSize: image.size) { size in
return image
}
}
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = playerItem.currentTime().seconds
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = playerItem.asset.duration.seconds
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate
// Set the metadata
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}