74
votes

I'm trying to play a sound with AVAudioPlayer but it won't work.

Edit 1: Still doesn't work.

Edit 2: This code works. My device was in silent mode.

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var audioPlayer = AVAudioPlayer()

    override func viewDidLoad() {
        super.viewDidLoad()

        var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("button-09", ofType: "wav"))
        println(alertSound)

        var error:NSError?
        audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
        audioPlayer.prepareToPlay()
        audioPlayer.play()
    }
}
8
Instead of passing "nil" to the error parameter, why not pass something in and find out what the error actually is?Michael Dautermann
the error contains niluser3722523
I'm practically using the exact same code as you for an osx application, and it all works except for the error code seems to be throwing some type of error itself: "fatal error: unexpectedly found nil while unwrapping an Optional value" which leads me to believe that there is something wrong with the error declaration. I don't know a lot about error in swift, but is this something you've run into before?Uzebeckatrente
@Uzebeckatrente - Check that you are not using NSURL(string:...). This returns nil for some reason in some circumstances. You should be using NSURL(fileURLWithPath:...) for file locations.djb
hahaha... kudos for silent mode!animal_chin

8 Answers

63
votes

Made modification to your code:

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var audioPlayer = AVAudioPlayer()

    override func viewDidLoad() {
        super.viewDidLoad()

        var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("button-09", ofType: "wav"))
        println(alertSound)

        // Removed deprecated use of AVAudioSessionDelegate protocol
        AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
        AVAudioSession.sharedInstance().setActive(true, error: nil)

        var error:NSError?
        audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
        audioPlayer.prepareToPlay()
        audioPlayer.play()
    }

}

Swift 3 and Swift 4.1:

import UIKit
import AVFoundation

class ViewController: UIViewController {
    private var audioPlayer: AVAudioPlayer?

    override func viewDidLoad() {
        super.viewDidLoad()

        let alertSound = URL(fileURLWithPath: Bundle.main.path(forResource: "button-09", ofType: "wav")!)
        print(alertSound)

        try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        try! AVAudioSession.sharedInstance().setActive(true)

        try! audioPlayer = AVAudioPlayer(contentsOf: alertSound)
        audioPlayer!.prepareToPlay()
        audioPlayer!.play()
    }
}
36
votes

As per Swift 2 the code should be

var audioPlayer : AVAudioPlayer!

func playSound(soundName: String)
{
    let coinSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(soundName, ofType: "m4a")!)
    do{
        audioPlayer = try AVAudioPlayer(contentsOfURL:coinSound)
        audioPlayer.prepareToPlay()//there is also an async version
        audioPlayer.play()
    }catch {
        print("Error getting the audio file")
    }
}
23
votes

Your audio play will be deallocated right after viewDidLoad finishes since you assign it to a local variable. You need to create a property for it to keep it around.

12
votes

The solution is for AudioPlayer which has play/pause/stop button in the navigation bar as bar button items

In Swift 2.1 you can use the below code

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var player:AVAudioPlayer = AVAudioPlayer()

    override func viewDidLoad() {
            super.viewDidLoad()

            let audioPath = NSBundle.mainBundle().pathForResource("ARRehman", ofType: "mp3")
            var error:NSError? = nil
            do {
                player = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath!))
            }
            catch {
                print("Something bad happened. Try catching specific errors to narrow things down")
            }

        }

    @IBAction func play(sender: UIBarButtonItem) {
        player.play()

    }


    @IBAction func stop(sender: UIBarButtonItem) {
        player.stop()
        print(player.currentTime)
        player.currentTime = 0
    }


    @IBAction func pause(sender: UIBarButtonItem) {

        player.pause()
    }


    @IBOutlet weak var sliderval: UISlider!


    @IBAction func slidermov(sender: UISlider) {

        player.volume = sliderval.value
        print(player.volume)
    }

}
7
votes

This will work.....

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var ding:AVAudioPlayer = AVAudioPlayer()

    override func viewDidLoad() {
        super.viewDidLoad()

        prepareAudios()
        ding.play()
    }

    func prepareAudios() {

        var path = NSBundle.mainBundle().pathForResource("ding", ofType: "mp3")
        ding = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: path!), error: nil)
        ding.prepareToPlay()
    }
}
2
votes

Use This Function to make sound in Swift (You can use this function where you want to make sound.)

First Add SpriteKit and AVFoundation Framework.

import SpriteKit
import AVFoundation
func playEffectSound(filename: String){
     runAction(SKAction.playSoundFileNamed("\(filename)", waitForCompletion: false))
}// use this function to play sound

playEffectSound("Sound File Name With Extension")
// Example :- playEffectSound("BS_SpiderWeb_CollectEgg_SFX.mp3")
2
votes

This is a pretty old question, but I did not see an answer that worked with Swift 3.0 so I modernized the code and added some safety checks to prevent crashes.

I also took the approach of pulling the playing part out into its own function to help with re-use for anyone coming across this answer.

import UIKit
import AVFoundation

class ViewController: UIViewController {
    var audioPlayer: AVAudioPlayer?

    override func viewDidLoad() {
        super.viewDidLoad()

        play(for: "button-09", type: "wav")
    }

    func play(for resource: String, type: String) {
        // Prevent a crash in the event that the resource or type is invalid
        guard let path = Bundle.main.path(forResource: resource, ofType: type) else { return }
        // Convert path to URL for audio player
        let sound = URL(fileURLWithPath: path)
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: sound)
            audioPlayer?.prepareToPlay()
            audioPlayer?.play()
        } catch {
            // Create an assertion crash in the event that the app fails to play the sound
            assert(false, error.localizedDescription)
        }
    }
}
2
votes

Swift 4.2 onwards, AudioSession category setup options have been changed. (Try following code for Swift 4.2 onwards)

import UIKit
import AVFoundation


class ViewController: UIViewController {


    var audioPlayer: AVAudioPlayer?

    override func viewDidLoad() {
        super.viewDidLoad()
        prepareAudioSession()
    }


    func prepareAudioSession() -> Void {
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: AVAudioSession.Mode.default, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
            try AVAudioSession.sharedInstance().setActive(true)

            prepareAudioPlayer()

        } catch {
            print("Issue with Audio Session")
        }
    }


    func prepareAudioPlayer() -> Void {

        let audioFileName = "test"
        let audioFileExtension = "mp3"

        guard let filePath = Bundle.main.path(forResource: audioFileName, ofType: audioFileExtension) else {
            print("Audio file not found at specified path")
            return
        }

        let alertSound = URL(fileURLWithPath: filePath)
        try? audioPlayer = AVAudioPlayer(contentsOf: alertSound)
        audioPlayer?.prepareToPlay()
    }


    func playAudioPlayer() -> Void {
        audioPlayer?.play()
    }


    func pauseAudioPlayer() -> Void {
        if audioPlayer?.isPlaying ?? false {
            audioPlayer?.pause()
        }
    }


    func stopAudioPlayer() -> Void {
        if audioPlayer?.isPlaying ?? false {
            audioPlayer?.stop()
        }
    }

    func resetAudioPlayer() -> Void {
        stopAudioPlayer()
        audioPlayer?.currentTime = 0
        playAudioPlayer()
    }

}