SashDing
SashDing

Reputation: 163

Sprite Kit no longer play sounds with iOS10

After installation on iPhone ios10, my ready to ship appstore app no longer play sounds! HELP! With ios9 everything worked perfectly

SKAction.playSoundFileNamed("Lose_bell.wav", waitForCompletion: false)

[476:72963] SKAction: Error playing sound resource

Upvotes: 3

Views: 853

Answers (2)

Jozey
Jozey

Reputation: 1740

I was REALLY annoyed by this problem, so I created my own custom class called JKAudioPlayer to temporarily avoid this problem and handle all my audio for me.

It that has 2 AVAudioPlayer objects. 1 to play music, and 1 to play sounds through it.

Here's the class.

import Foundation
import SpriteKit
import AVFoundation

/**Manages a shared instance of JKAudioPlayer.*/
private let JKAudioInstance = JKAudioPlayer()

/**Provides an easy way to play sounds and music. Use sharedInstance method to access
   a single object for the entire game to manage the sound and music.*/
open class JKAudioPlayer {

    /**Used to access music.*/
    var musicPlayer: AVAudioPlayer!
    var soundPlayer: AVAudioPlayer!

    /** Allows the audio to be shared with other music (such as music being played from your music app).
     If this setting is false, music you play from your music player will stop when this app's music starts.
     Default set by Apple is false. */
    static var canShareAudio = false {
        didSet {
            canShareAudio ? try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
                : try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
        }
    }

    /**Creates an instance of the JAAudio class so the user doesn't have to make
       their own instance and allows use of the functions. */
    open class func sharedInstance() -> JKAudioPlayer {
        return JKAudioInstance
    }

    /**Plays music. You can ignore the "type" property if you include the full name with extension
       in the "filename" property. Set "canShareAudio" to true if you want other music to be able
       to play at the same time (default by Apple is false).*/
    open func playMusic(_ fileName: String, withExtension type: String = "") {
        if let url = Bundle.main.url(forResource: fileName, withExtension: type) {
            musicPlayer = try? AVAudioPlayer(contentsOf: url)
            musicPlayer.numberOfLoops = -1
            musicPlayer.prepareToPlay()
            musicPlayer.play()
        }
    }

    /**Stops the music. Use the "resumeMusic" method to turn it back on. */
    open func stopMusic() {
        if musicPlayer != nil && musicPlayer!.isPlaying {
            musicPlayer.currentTime = 0
            musicPlayer.stop()
        }
    }

    /**Pauses the music. Use the "resumeMusic" method to turn it back on. */
    open func pauseMusic() {
        if musicPlayer != nil && musicPlayer!.isPlaying {
            musicPlayer.pause()
        }
    }

    /**Resumes the music after being stopped or paused. */
    open func resumeMusic() {
        if musicPlayer != nil && !musicPlayer!.isPlaying {
            musicPlayer.play()
        }
    }

    /**Plays a single sound.*/
    open func playSoundEffect(named fileName: String) {
        if let url = Bundle.main.url(forResource: fileName, withExtension: "") {
            soundPlayer = try? AVAudioPlayer(contentsOf: url)
            soundPlayer.stop()
            soundPlayer.numberOfLoops = 1
            soundPlayer.prepareToPlay()
            soundPlayer.play()
        }
    }
}

Here's how I use it. First, create a global variable to handle the audio (or just sound if you want).

let audio = JKAudioPlayer.sharedInstance()

Then, whenever you want to play a sound, then just do this.

audio.playSoundEffect(named: "SoundFileName.type")

And your sound should play. You have to have the type of the file in the string. I completely got rid of ALL SKActions that play sound and changed them to my code. It all works perfect now and NO errors. I used this class in conjunction with my JKButtonNode to play sounds too.

And for the record, I made all my sound files from .mp3 to .wav. I don't know if that is what makes it work, but I switched them all at the time I was having SKAction errors.

Also, I added a shortcut just for me. If you want the user to be able to play their music while playing their game, you can add this code in your GameViewController.

JKAudioPlayer.canShareAudio = true

Upvotes: 2

SashDing
SashDing

Reputation: 163

Please correct my English please, I'm sorry. I found what the problem is. Previously, the action did not end until it completes all its constituent elements. iOS10 not consider sound playing an important matter, and discards it as soon as completed other action items. For example, this option will not perform audio snippet:

run(SKAction.sequence([SKAction.playSoundFileNamed("Lose_bell.wav", waitForCompletion: fals), SKAction.removeFromParent()]))

But this time to fulfill:

run(SKAction.sequence([SKAction.playSoundFileNamed("Lose_bell.wav", waitForCompletion: fals),SKAction.wait(forDuration: 5.0), SKAction.removeFromParent()]))

Is it possible to somehow keep waiting performing stocks? The method waitForCompletion: true does not work.

Upvotes: 2

Related Questions