3

I'm making a Flappy Bird-type game, and I want to add a sound effect for when a coin is collected. This is what I have:

import SpriteKit
import AVFoundation

class GameScene: SKScene {

    var coinSound = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("Super Mario Bros.- Coin Sound Effect", ofType: "mp3")!)
    var audioPlayer = AVAudioPlayer()

Later in my didMoveToView:

audioPlayer = AVAudioPlayer(contentsOfURL: coinSound, error: nil)
audioPlayer.prepareToPlay()

For the second part (in my didMoveToView), the first line keeps showing an error:

Call can throw, but it is not marked with 'try' and the error is not handled

How do I fix this?

nhgrif
  • 61,578
  • 25
  • 134
  • 173
swagger
  • 73
  • 1
  • 10
  • 1
    have a look at this post: http://stackoverflow.com/questions/24393495/playing-a-sound-with-avaudioplayer – JanR Mar 29 '16 at 01:28
  • does this, when corrected by the answer, actually play a sound? – Confused Nov 11 '16 at 16:00
  • I know you already marked an answer but I would actually use SKAction.playSound... for short 1 time sounds. AVFoundation is better used for background music. – crashoverride777 Nov 11 '16 at 21:24

3 Answers3

15

I know you already have marked an answer but if you re read this question or for future readers its easier to use SKActions to play a short 1 time sound.

AVFoundation is better used for background music.

e.g

class GameScene: SKScene {

     // add as class property so sound is preloaded and always ready
     let coinSound = SKAction.playSoundFileNamed("NAMEOFSOUNDFILE", waitForCompletion: false)
}

and than when you need to play the sound simply say this

run(coinSound)

Hope this helps

crashoverride777
  • 10,581
  • 2
  • 32
  • 56
  • I'm using this way, now. It's a SHIT TONNE easier, and if the action is precreated, seems to be caching the audio file so there's no loading/lag on the first play. – Confused Nov 11 '16 at 22:30
  • Yeah it is. I did not actually realise this was a 7 month old question, thought it was new. – crashoverride777 Nov 11 '16 at 22:31
2
audioPlayer = AVAudioPlayer(contentsOfURL: coinSound, error: nil)

This is no longer the way that errors are handled in Swift. You must use the do-try-catch syntax.

do {
    audioPlayer = try AVAudioPlayer(contentsOfURL: coinSound)
    audioPlayer.prepareToPlay()
}
catch let error {
    // handle error
}

Alternatively, if you don't care to write any error handling code, you can make your audioPlayer variable optional and write a failable try?:

audioPlayer = try? AVAudioPlayer(contentsOfURL: coinSound)
audioPlayer?.prepareToPlay()

This is more closely equivalent to the code you're attempting where you pass nil for the error parameter.

Or if crashing is the appropriate behavior when an error occurs, you could take this approach:

guard let audioPlayer = try? AVAudioPlayer(contentsOfURL: coinSound) else {
    fatalError("Failed to initialize the audio player with asset: \(coinSound)")
}
audioPlayer.prepareToPlay()
self.audioPlayer = audioPlayer
nhgrif
  • 61,578
  • 25
  • 134
  • 173
  • If you get a moment, and are inclined, could you answer the question in my comment in the swift 3 version below? – Confused Nov 11 '16 at 16:25
  • An alternative for immediate crashing is `let audioPlayer = try! etc...`. You don't get the detailed error message, but if you're debugging then Xcode will pinpoint the location anyway. – Andrew Duncan Oct 02 '18 at 17:01
0

Swift 3.0+

For the above guard let example from @nhgrif, this Swift 3.0+ way of doing it:

var coinSound = NSURL(fileURLWithPath:Bundle.main.path(forResource: "Super Mario Bros.- Coin Sound Effect", ofType: "mp3")!)
var audioPlayer = AVAudioPlayer()

func playCoinSound(){
    guard let soundToPlay = try? AVAudioPlayer(contentsOf: coinSound as URL) else {
        fatalError("Failed to initialize the audio player with asset: \(coinSound)")
    }
    soundToPlay.prepareToPlay()

//  soundToPlay.play() // NO IDEA WHY THIS DOES NOT WORK!!!???

    self.audioPlayer = soundToPlay
    self.audioPlayer.play() // But this does work... please let me know why???
}

playCoinSound()
Confused
  • 6,048
  • 6
  • 34
  • 75
  • I have no idea. I just copied that from all the other guards I have in my (increasingly massive) reference files. Which was from above, the answer by nhgrif. I hadn't used it until today, at which point I found out about the swift 3 changes, and got curious about the need to repeat myself to get it to play. – Confused Nov 11 '16 at 22:28
  • Thats cool just asking. Probably better to use return in this case and exit gracefully so your app doesn't crash. – crashoverride777 Nov 11 '16 at 22:30