Reputation: 8767
I'm using AVPlayer
to play audio from a URL
. Everything works fine, but I don't know how to stop current Audio playing when I am in a different ViewController
I don't want to call player.pause()
in viewWillDisappear()
because i want to stop it in specific cases, but I want still to play the audio in the background when the app is inactive.
I have this turned on in Xcode app settings for background playback when the app is inactive
2nd question: Can I improve this class or is everything ok?
protocol AVPlayerServiceDelegate {
func playerDidUpdateCurrentPlayingTime(_ time: CMTime)
}
class AVPlayerService {
static let instance = AVPlayerService()
private var audioPlayer: AVPlayer!
public weak var delegate: AVPlayerServiceDelegate?
func setupPlayer(forURL url: URL) {
let playerItem: AVPlayerItem = AVPlayerItem(url: url)
audioPlayer = AVPlayer(playerItem: playerItem)
audioPlayer.play()
audioPlayer.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: DispatchQueue.main) { (CMTime) -> Void in
if self.audioPlayer.currentItem?.status == .readyToPlay {
self.delegate?.playerDidUpdateCurrentPlayingTime(self.audioPlayer.currentTime())
}
}
}
var isPlaying: Bool {
return audioPlayer.isPlaying
}
var isPaused: Bool {
return audioPlayer.isPaused
}
var rate: Float {
return audioPlayer.rate
}
func seek(to time: CMTime) {
audioPlayer.seek(to: time)
}
func seekToBeginning() {
audioPlayer.seek(to: CMTimeMake(value: 0, timescale: 1))
}
func replay() {
audioPlayer.seek(to: CMTimeMake(value: 0, timescale: 1))
audioPlayer.play()
}
func pause() {
if let audioPlayer = audioPlayer {
audioPlayer.pause()
}
}
func play() {
audioPlayer.play()
}
func freshInstance() {
audioPlayer = AVPlayer()
}
var assetDuration: CMTime {
get {
return audioPlayer.currentItem!.asset.duration
}
}
}
Upvotes: 1
Views: 141
Reputation: 20274
You need a mechanism to talk to the player
object in order to perform actions on it.
There are many ways to go about it.
Personally, I would create a dedicated class for the player that deals with instantiating it, loading the audio resource, playing, pausing, etc.
Then I would either have this class be a singleton so I can access it from anywhere, or inject it and pass it onto other classes where it will be used.
class MyAudioPlayer {
static let shared = MyAudioPlayer()
private var player: AVAudioPlayer?
func load(_ url: URL) {
player = try? AVAudioPlayer(contentsOf: url)
}
func play(url: URL) {
load(url: url)
player?.play()
}
func pause() {
player?.pause()
}
func stop() {
player?.stop()
}
}
Send actions from anywhere within app:
MyAudioPlayer.shared.play(someURL)
MyAudioPlayer.shared.pause()
As for playing in background even when app is not active will require (maybe in AppDelegate
)
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback,
with: .duckOthers)
try? AVAudioSession.sharedInstance().setActive(true)
UIApplication.shared.beginReceivingRemoteControlEvents()
Upvotes: 2