TruMan1
TruMan1

Reputation: 36138

Reusable extension to play sound without memory leak in iOS and Swift?

Playing a sound is so verbose in code and would like to create an extension of AVAudioSession if possible. The way I'm doing it is assigning an object to a variable, but need help/advise on how to set up this function so it's reusable and optimized.

Here's what I have:

func playSound(name: String, extension: String = "mp3") {
    let sound = NSBundle.mainBundle().URLForResource(name, withExtension: extension)

    do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        try AVAudioSession.sharedInstance().setActive(true)

        UIApplication.sharedApplication().beginReceivingRemoteControlEvents()

        audioPlayer = try AVAudioPlayer(contentsOfURL: sound!)
        audioPlayer?.prepareToPlay()
        audioPlayer?.play()
    } catch { }
}

I think I have to create the audioPlayer variable outside of this function, otherwise I had a hard time playing anything. Maybe it can be self contained? I'm hoping to use it something like this:

AVAudioSession.sharedInstance().play("bebop")

Upvotes: 3

Views: 1004

Answers (2)

Alexander Dvornikov
Alexander Dvornikov

Reputation: 1084

Taking the code straight from your example I see two options:

1) Is there any particular reason why you want to make it as an extension of AVAudioSession? If not, just make your own service!

class AudioPlayerService {

    static let sharedInstance = AudioPlayerService()

    var audioPlayer: AVAudioPlayer?

    func playSound(name: String, extension: String = "mp3") {
        let sound = NSBundle.mainBundle().URLForResource(name, withExtension: extension)

        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            try AVAudioSession.sharedInstance().setActive(true)

            UIApplication.sharedApplication().beginReceivingRemoteControlEvents()

            audioPlayer = try AVAudioPlayer(contentsOfURL: sound!)
            audioPlayer?.prepareToPlay()
            audioPlayer?.play()
        } catch { }
    }

}

2) If you do need to make it as an extension of AVAudioSession, then take a look at associated objects

extension AVAudioSession {

    private struct AssociatedKeys {
        static var AudioPlayerTag = "AudioPlayerTag"
    }

    var audioPlayer: AVAudioPlayer? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.AudioPlayerTag) as? AVAudioPlayer
        }

        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.AudioPlayerTag,
                    newValue as AVAudioPlayer?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }

    func playSound(name: String, extension: String = "mp3") {
        let sound = NSBundle.mainBundle().URLForResource(name, withExtension: extension)

        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            try AVAudioSession.sharedInstance().setActive(true)

            UIApplication.sharedApplication().beginReceivingRemoteControlEvents()

            audioPlayer = try AVAudioPlayer(contentsOfURL: sound!)
            audioPlayer?.prepareToPlay()
            audioPlayer?.play()
        } catch { }
    }

}

Upvotes: 3

Timmy Sorensen
Timmy Sorensen

Reputation: 570

This is the best way I have found to add sound

Filename is shootMissile.wav

func shootMissileSound() {
    if let soundURL = NSBundle.mainBundle().URLForResource("shootMissile", withExtension: "wav") {
        var mySound: SystemSoundID = 0
        AudioServicesCreateSystemSoundID(soundURL, &mySound)
        AudioServicesPlaySystemSound(mySound);
    }
}

Upvotes: 1

Related Questions