SwiftiSwift
SwiftiSwift

Reputation: 8767

Turn off audio playing?

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 inactiveenter image description here

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

Answers (1)

staticVoidMan
staticVoidMan

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.

Basic Example:

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

Related Questions