248

I would like to play a sound using Swift.

My code worked in Swift 1.0 but now it doesn't work anymore in Swift 2 or newer.

override func viewDidLoad() {
  super.viewDidLoad()

  let url:NSURL = NSBundle.mainBundle().URLForResource("soundName", withExtension: "mp3")!

  do { 
    player = try AVAudioPlayer(contentsOfURL: url, fileTypeHint: nil) 
  } catch _{
    return
  }

  bgMusic.numberOfLoops = 1
  bgMusic.prepareToPlay()

  if (Data.backgroundMenuPlayed == 0){
    player.play()
    Data.backgroundMenuPlayed = 1
  }
}
John Slegers
  • 45,213
  • 22
  • 199
  • 169
Michel Kansou
  • 2,851
  • 4
  • 13
  • 22
  • 1
    Take a look at [SwiftySound](https://github.com/adamcichy/SwiftySound). More details in [this answer](http://stackoverflow.com/a/42392605/730701). – Adam Feb 22 '17 at 13:20
  • If you just want a sound from the system then see: [Using existing system sounds in iOS App](https://stackoverflow.com/questions/31126124/using-existing-system-sounds-in-ios-app-swift) – mfaani Mar 13 '19 at 18:43

19 Answers19

442

Most preferably you might want to use AVFoundation. It provides all the essentials for working with audiovisual media.

Update: Compatible with Swift 2, Swift 3 and Swift 4 as suggested by some of you in the comments.


Swift 2.3

import AVFoundation

var player: AVAudioPlayer?

func playSound() {
    let url = NSBundle.mainBundle().URLForResource("soundName", withExtension: "mp3")!

    do {
        player = try AVAudioPlayer(contentsOfURL: url)
        guard let player = player else { return }

        player.prepareToPlay()
        player.play()

    } catch let error as NSError {
        print(error.description)
    }
}

Swift 3

import AVFoundation

var player: AVAudioPlayer?

func playSound() {
    guard let url = Bundle.main.url(forResource: "soundName", withExtension: "mp3") else { return }

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

        let player = try AVAudioPlayer(contentsOf: url)

        player.play()

    } catch let error {
        print(error.localizedDescription)
    }
}

Swift 4 (iOS 13 compatible)

import AVFoundation

var player: AVAudioPlayer?

func playSound() {
    guard let url = Bundle.main.url(forResource: "soundName", withExtension: "mp3") else { return }

    do {
        try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)            
        try AVAudioSession.sharedInstance().setActive(true)

        /* The following line is required for the player to work on iOS 11. Change the file type accordingly*/
        player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)

        /* iOS 10 and earlier require the following line:
        player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3) */

        guard let player = player else { return }

        player.play()

    } catch let error {
        print(error.localizedDescription)
    }
}

Make sure to change the name of your tune as well as the extension. The file needs to be properly imported (Project Build Phases > Copy Bundle Resources). You might want to place it in assets.xcassets for greater convenience.

For short sound files you might want to go for non-compressed audio formats such as .wav since they have the best quality and a low cpu impact. The higher disk-space consumption should not be a big deal for short sound files. The longer the files are, you might want to go for a compressed format such as .mp3 etc. pp. Check the compatible audio formats of CoreAudio.


Fun-fact: There are neat little libraries which make playing sounds even easier. :)
For example: SwiftySound

Devapploper
  • 6,062
  • 3
  • 20
  • 41
  • Sorry but this code doesn't work anymore in swift 2.0 it prompt an error " Call can throw, but it is not marked with 'try' and the error is not handled" – Michel Kansou Aug 16 '15 at 15:23
  • 2
    Replace `bgMusic = AVAudioPlayer(contentsOfURL: bgMusicURL, fileTypeHint: nil)` with `do { bgMusic = try AVAudioPlayer(contentsOfURL: bgMusicURL, fileTypeHint: nil) } catch _ { return \\ if it doesn't exist, don't play it}` – saagarjha Aug 16 '15 at 17:28
  • Based on your code, it should *never* print anything, since return is called before the print. – saagarjha Aug 16 '15 at 18:02
  • I just change loaded another music file with .m4a and it work – Michel Kansou Aug 16 '15 at 18:03
  • 22
    I had to make the AVAudioPlayer object an instance variable to get this to work. As a local variable it wouldn't play anything, no error. Delegates wouldn't get called, either. – Kaleb Mar 26 '16 at 18:59
  • Works with 3.0 as well – danielsalare Oct 04 '16 at 00:28
  • 2
    Why do you use a guard here? `player = try AVAudioPlayer(contentsOf: url) guard let player = player else { return }` seems like extra work to me, why not just `let player = try AVAudioPlayer(contentsOf: url)`? – xandermonkey Dec 19 '16 at 14:56
  • 2
    Using a guard statement makes you pretty safe from crashing due to nil value. – Aashish Mar 01 '17 at 12:12
  • @Aashish yep, forgot to reply – Devapploper Mar 01 '17 at 20:03
  • @Devapploper do you think you could help with my question: https://stackoverflow.com/questions/44201592/swift-3-stream-mp3-file-from-server ? – JamesG May 26 '17 at 12:27
  • What are the acceptable file types? Can it be `aiff`? – mfaani Jul 18 '17 at 20:50
  • @Honey I updated the answer. Take a look at this: https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/CoreAudioOverview/SupportedAudioFormatsMacOSX/SupportedAudioFormatsMacOSX.html – Devapploper Jul 26 '17 at 09:39
  • What if the audio file is not mp3 and it's OGG or DASH? – Pavlos Feb 01 '18 at 13:55
  • What is the meaning of the ** player.prepareToPlay() player.play()** one after an other prepareToPlay() does nothing here! – Hope Aug 08 '18 at 10:01
  • Depending on the behaviour you wish to achieve, it’s worth to take a deeper look at the available categories: https://developer.apple.com/documentation/avfoundation/avaudiosession/1616591-availablecategories i.e.: I think in most cases the category `Ambient` would be a better fit, since it respects the devices mute button. – ixany Aug 09 '18 at 21:37
  • MISTAKE: `iOS 10 and earlier require the following line: ...`. No, xCode won't even compile the string from comments. Maybe did you mean a specific iOS sdk version? – Vyachaslav Gerchicov Oct 16 '18 at 12:27
  • On Swift 4.2.0 and Xcode 10.1, I'm getting a few errors with the first try statement: `try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)` first `Replace 'AVAudioSessionCategoryPlayback' with 'AVAudioSession.Category.playback'` then `setCategory is unavailable in Swift` what worked for me was `try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])` – waffl Jan 05 '19 at 18:49
  • gives a String member error in Swift 4.2: try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default) – SoTm May 20 '19 at 19:37
  • Thanks. Below two lines helped alot try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default) try AVAudioSession.sharedInstance().setActive(true) – Mahesh Peri Aug 29 '19 at 09:01
  • `try AVAudioSession.sharedInstance().setActive(true)` save me! – nastassia Oct 30 '19 at 09:46
  • Audio did not played properly when this function was made as an extension of UIViewController by passing AVAudioPlayer as argument, What could be the issue? It works well as a normal function – Atul Vasudev A Mar 31 '20 at 06:52
52

Swift version: 5.4 and Above

import AVFoundation
var player: AVAudioPlayer?

func playSound() {
    guard let path = Bundle.main.path(forResource: "beep", ofType:"mp3") else {
        return }
    let url = URL(fileURLWithPath: path)

    do {
        player = try AVAudioPlayer(contentsOf: url)
        player?.play()
        
    } catch let error {
        print(error.localizedDescription)
    }
}
49

For Swift 3 :

import AVFoundation

/// **must** define instance variable outside, because .play() will deallocate AVAudioPlayer 
/// immediately and you won't hear a thing
var player: AVAudioPlayer?

func playSound() {
    guard let url = Bundle.main.url(forResource: "soundName", withExtension: "mp3") else {
        print("url not found")
        return
    }

    do {
        /// this codes for making this app ready to takeover the device audio
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        try AVAudioSession.sharedInstance().setActive(true)

        /// change fileTypeHint according to the type of your audio file (you can omit this)

        player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3)

        // no need for prepareToPlay because prepareToPlay is happen automatically when calling play()
        player!.play()
    } catch let error as NSError {
        print("error: \(error.localizedDescription)")
    }
}

The best practice for local assets is to put it inside assets.xcassets and you load the file like this :

func playSound() {
    guard let url = Bundle.main.url(forResource: "soundName", withExtension: "mp3") else {
        print("url not found")
        return
    }

    do {
        /// this codes for making this app ready to takeover the device audio
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        try AVAudioSession.sharedInstance().setActive(true)

        /// change fileTypeHint according to the type of your audio file (you can omit this)

        /// for iOS 11 onward, use :
        player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)

        /// else :
        /// player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3)

        // no need for prepareToPlay because prepareToPlay is happen automatically when calling play()
        player!.play()
    } catch let error as NSError {
        print("error: \(error.localizedDescription)")
    }
}
Adi Nugroho
  • 860
  • 10
  • 12
  • This code worked for me on iOS 10.2.1, xCode 8.2.1. For me, the two "try AVAudioSession" lines made the difference between the sounds actually being heard on a real device or not. Without them, no sound. – pvanallen Jan 27 '17 at 21:58
  • This helped me a ton, i'm having some trouble understanding what happens in the `do` block though. obviously `player!.play()` is self explanatory. But what is the purpose of the `setCategory` and `setActive` methods? – Shan Robertson Feb 03 '17 at 00:00
  • 2
    If you provide `AVAudioSessionCategoryPlayback` to `setCategory`, it will ensure the audio is always played even though phone is in lock screen or in silent mode. `setActive` is like telling the system that your app is ready to play audio – Adi Nugroho Feb 03 '17 at 10:40
  • @AdiNugroho do you think you could help with my question: https://stackoverflow.com/questions/44201592/swift-3-stream-mp3-file-from-server ? – JamesG May 26 '17 at 12:27
  • I'm having a lot of trouble with this on iOS 11. It used to work but now it suddenly doesn't. Any ideas? – nickdnk Oct 05 '17 at 16:02
  • @nickdnk . For iOS 11 onward, change AVAudioPlayer initialization into `player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)` – Adi Nugroho Oct 07 '17 at 06:18
  • I use Objective-C :( @AdiNugroho – nickdnk Oct 07 '17 at 13:45
  • This helped me with iOS 11. Thank you for keeping this answer up to date – WBuck Dec 16 '17 at 19:34
22

iOS 12 - Xcode 10 beta 6 - Swift 4.2

Use just 1 IBAction and point all the buttons to that 1 action.

import AVFoundation

var player = AVAudioPlayer()

@IBAction func notePressed(_ sender: UIButton) {
    print(sender.tag) // testing button pressed tag
    let path = Bundle.main.path(forResource: "note\(sender.tag)", ofType : "wav")!
    let url = URL(fileURLWithPath : path)
    do {
        player = try AVAudioPlayer(contentsOf: url)
        player.play()
    } catch {
        print ("There is an issue with this code!")
    }
}
Wimukthi Rajapaksha
  • 961
  • 1
  • 11
  • 23
Tony Ward
  • 233
  • 2
  • 6
  • 12
    It's funny that you're assuming that everyone are watching "iOS 11 & Swift 4 - The Complete iOS App Development Bootcamp" – karlingen Apr 28 '19 at 07:01
17

If code doesn't generate any error, but you don't hear sound - create the player as an instance:

   static var player: AVAudioPlayer!

For me the first solution worked when I did this change :)

EranKT
  • 325
  • 1
  • 4
  • 15
  • Works for me. Does anybody know why do you have to set this to static? – kuzdu Aug 19 '18 at 09:52
  • 4
    I don't think it has to be static (anymore?), but it seems perhaps if you let it go out of scope after it's created, even if you've called play(), it won't play? I just made it an instance variable of my class and it works. – biomiker Oct 23 '18 at 22:54
  • 6
    @kuzdu This is due to you not placing the `player` in the outside scope. Otherwise, `player` gets delocalised and therefore cannot play any sound as it no longer exists. – George Jan 02 '19 at 21:29
  • Worked for me - without `static` – Todd Feb 11 '20 at 15:59
  • Worked for me too! Thanks! – Maverick447 Feb 18 '21 at 18:57
14

Swift 4, 4.2 and 5

Play audio from URL and from your project (Local File)

import UIKit
import AVFoundation

class ViewController: UIViewController{

var audioPlayer : AVPlayer!

override func viewDidLoad() {
        super.viewDidLoad()
// call what ever function you want.
    }

    private func playAudioFromURL() {
        guard let url = URL(string: "https://geekanddummy.com/wp-content/uploads/2014/01/coin-spin-light.mp3") else {
            print("error to get the mp3 file")
            return
        }
        do {
            audioPlayer = try AVPlayer(url: url as URL)
        } catch {
            print("audio file error")
        }
        audioPlayer?.play()
    }

    private func playAudioFromProject() {
        guard let url = Bundle.main.url(forResource: "azanMakkah2016", withExtension: "mp3") else {
            print("error to get the mp3 file")
            return
        }

        do {
            audioPlayer = try AVPlayer(url: url)
        } catch {
            print("audio file error")
        }
        audioPlayer?.play()
    }

}
Akbar Khan
  • 2,215
  • 19
  • 27
6

For Swift 5 "AVFoundation"\

Simple code without error handling to play audio from your local path

import AVFoundation
var audio:AVPlayer!

func stopAlarm() {
    // To pause or stop audio in swift 5 audio.stop() isn't working
    audio.pause()
}

func playAlarm() {
    // need to declare local path as url
    let url = Bundle.main.url(forResource: "Alarm", withExtension: "mp3")
    // now use declared path 'url' to initialize the player
    audio = AVPlayer.init(url: url!)
    // after initialization play audio its just like click on play button
    audio.play()
}

enter image description here

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Sadiq Qasmi
  • 71
  • 1
  • 3
6

it is simple and get's the job done!

import AVFoundation
var player: AVAudioPlayer!
            
    let url = Bundle.main.url(forResource: "sound_name", withExtension: "mp3")
     
    player = try! AVAudioPlayer(contentsOf: url!)
    player.play()
Ansub
  • 501
  • 7
  • 11
4

Swift 3

import AVFoundation


var myAudio: AVAudioPlayer!

    let path = Bundle.main.path(forResource: "example", ofType: "mp3")!
    let url = URL(fileURLWithPath: path)
do {
    let sound = try AVAudioPlayer(contentsOf: url)
    myAudio = sound
    sound.play()
} catch {
    // 
}

//If you want to stop the sound, you should use its stop()method.if you try to stop a sound that doesn't exist your app will crash, so it's best to check that it exists.

if myAudio != nil {
    myAudio.stop()
    myAudio = nil
}
Saranjith
  • 11,242
  • 5
  • 69
  • 122
Mr.Shin
  • 89
  • 3
4

This is basic code to find and play an audio file in Swift.

Add your audio file to your Xcode and add the code below.

import AVFoundation

class ViewController: UIViewController {

   var audioPlayer = AVAudioPlayer() // declare globally

   override func viewDidLoad() {
        super.viewDidLoad()

        guard let sound = Bundle.main.path(forResource: "audiofilename", ofType: "mp3") else {
            print("Error getting the mp3 file from the main bundle.")
            return
        }
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: sound))
        } catch {
            print("Audio file error.")
        }
        audioPlayer.play()
    }

    @IBAction func notePressed(_ sender: UIButton) { // Button action
        audioPlayer.stop()
    }
}
Alex Zavatone
  • 4,106
  • 36
  • 54
M VIJAY
  • 149
  • 1
  • 6
4

Game style:

file Sfx.swift

import AVFoundation

public let sfx = Sfx.shared
public final class Sfx: NSObject {
    
    static let shared = Sfx()
    
    var apCheer: AVAudioPlayer? = nil
    // etc
    
    private override init() {
        guard let s = Bundle.main.path(forResource: "cheer", ofType: "mp3") else { print("Sfx woe"); return }
        do { apCheer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: s)) } catch { print("Sfx woe"); return }
        // etc
    }
    
    func cheer() { apCheer?.play() }
    func plonk() { apPlonk?.play() }
    func crack() { apCrack?.play() }
    // etc
}

Simply have one of these ...

    var apCheer: AVAudioPlayer? = nil

for each of your audio stings. So you may have 5, 20, 10, whatever.

For each of those, simply copy/paste the two lines of initialization code:

guard let s = Bundle.main.path(forResource: "cheer", ofType: "mp3") else { return print("Sfx woe") }
do { apCheer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: s)) } catch { return print("Sfx woe") }

Then, anywhere at all, in any code, in any file, you can:

sfx.cheer()
sfx.crack()
Fattie
  • 27,874
  • 70
  • 431
  • 719
3

Tested with Swift 4 and iOS 12:

import UIKit
import AVFoundation
class ViewController: UIViewController{
    var player: AVAudioPlayer!
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func playTone(number: Int) {
        let path = Bundle.main.path(forResource: "note\(number)", ofType : "wav")!
        let url = URL(fileURLWithPath : path)
        do {
            player = try AVAudioPlayer(contentsOf: url)
            print ("note\(number)")
            player.play()
        }
        catch {
            print (error)
        }
    }

    @IBAction func notePressed(_ sender: UIButton) {
        playTone(number: sender.tag)
    }
}
karel
  • 5,489
  • 46
  • 45
  • 50
Ershin Bot
  • 41
  • 2
3
import AVFoundation
var player:AVAudioPlayer!

func Play(){
    guard let path = Bundle.main.path(forResource: "KurdishSong", ofType: "mp3")else{return}
    let soundURl = URL(fileURLWithPath: path)
    player = try? AVAudioPlayer(contentsOf: soundURl)
    player.prepareToPlay()
    player.play()
    //player.pause()
    //player.stop()
}
Wimukthi Rajapaksha
  • 961
  • 1
  • 11
  • 23
1

First import these libraries

import AVFoundation

import AudioToolbox    

set delegate like this

   AVAudioPlayerDelegate

write this pretty code on button action or something action:

guard let url = Bundle.main.url(forResource: "ring", withExtension: "mp3") else { return }
    do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        try AVAudioSession.sharedInstance().setActive(true)
        player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
        guard let player = player else { return }

        player.play()
    }catch let error{
        print(error.localizedDescription)
    }

100% working in my project and tested

Mr.Javed Multani
  • 12,549
  • 4
  • 53
  • 52
1

Swift 4 (iOS 12 compatible)

var player: AVAudioPlayer?

let path = Bundle.main.path(forResource: "note\(sender.tag)", ofType: "wav")
let url = URL(fileURLWithPath: path ?? "")
    
do {
   player = try AVAudioPlayer(contentsOf: url)
   player?.play()
} catch let error {
   print(error.localizedDescription)
}
Community
  • 1
  • 1
imdpk
  • 29
  • 1
  • I get the following error: `The operation couldn’t be completed. (OSStatus error 1954115647.)`. I have looked everywhere, and cannot find a solution. Might post a question about it. – George Jan 02 '19 at 21:46
1
import UIKit
import AVFoundation

class ViewController: UIViewController{

    var player: AVAudioPlayer?

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

    @IBAction func notePressed(_ sender: UIButton) {

        guard let url = Bundle.main.url(forResource: "note1", withExtension: "wav") else { return }

        do {
            try AVAudioSession.sharedInstance().setCategory((AVAudioSession.Category.playback), mode: .default, options: [])
            try AVAudioSession.sharedInstance().setActive(true)


            /* The following line is required for the player to work on iOS 11. Change the file type accordingly*/
            player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)

            /* iOS 10 and earlier require the following line:
             player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3) *//

            guard let player = player else { return }

            player.play()

        } catch let error {
            print(error.localizedDescription)
        }

    }

}
garritfra
  • 546
  • 4
  • 21
0
var soundEffect = AVAudioPlayer()

func playSound(_ buttonTag : Int){

    let path = Bundle.main.path(forResource: "note\(buttonTag)", ofType : "wav")!
    let url = URL(fileURLWithPath : path)

    do{
        soundEffect = try AVAudioPlayer(contentsOf: url)
        soundEffect?.play()
        // to stop the spound .stop()
    }catch{
        print ("file could not be loaded or other error!")
    }
}

works in swift 4 latest version. ButtonTag would be a tag on a button on your interface. Notes are in a folder in a folder parallel to Main.storyboard. Every note is named as note1, note2, etc. ButtonTag is giving the number 1, 2, etc from the button clicked which is passed as param

Mohit Kumar
  • 2,898
  • 3
  • 21
  • 34
0
import AVFoundation

import AudioToolbox

public final class MP3Player : NSObject {
    
    // Singleton class
    static let shared:MP3Player = MP3Player()
    
    private var player: AVAudioPlayer? = nil
    
    // Play only mp3 which are stored in the local
    public func playLocalFile(name:String) {
        guard let url = Bundle.main.url(forResource: name, withExtension: "mp3") else { return }

        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
            try AVAudioSession.sharedInstance().setActive(true)
            player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
            guard let player = player else { return }

            player.play()
        }catch let error{
            print(error.localizedDescription)
        }
    }
}

To call this function

MP3Player.shared.playLocalFile(name: "JungleBook")
Shivam Parmar
  • 1,520
  • 11
  • 27
Nirbhay Singh
  • 1,204
  • 1
  • 12
  • 16
0

Using SKActions...

// Preload the sound(s)
private let buttonSound = SKAction.playSoundFileNamed("SoundButton.wav", waitForCompletion: false)

// Play using run function
buttonNode?.run(buttonSound)
Mateus
  • 357
  • 1
  • 4
  • 14