Piotr979
Piotr979

Reputation: 239

my iOS SwiftUI project doesn't play sounds with AVAudioPlayer

I have a stupid issue but for some reason I can't resolve it. I try to play sound with AVAudioPlayer but can't hear anything. There are no errors, but still my app is silent. Its built with SwiftUI. This is a piece of code:

struct SoundsListView: View {
    
   var body: some View {
        List(Helpers.shared.sounds, id:\.self) { item in
            Text(item)
            Spacer()
            Button(action: {
                var soundPlayer: AVAudioPlayer?
                guard let audioFile: URL = Bundle.main.url(forResource: "Analog watch", withExtension: "mp3") else { return }
                do {
                    soundPlayer = try AVAudioPlayer(contentsOf: audioFile)
                    print(soundPlayer)
                    guard let player = soundPlayer else { return }
                   player.play()
                } catch let error {
                    print("Cannot play sound")
                }
            }) {
                Image(systemName: "play")
            } //: BUTTON
        }
    }
} 

There are no errors, also sound files have target membership set. (What is strange it's that the same piece of code works with my older project, but written in Swift + Storyboards)

There is no sound generated neither in simulator nor hardware device. Silent mode is off, volume set to max. I have no idea what's wrong here.

BTW: This app sends notifications to user and sound is OK. But not in this piece of code

Upvotes: 1

Views: 3194

Answers (2)

Dave Levy
Dave Levy

Reputation: 1172

For me the audio would only work sometimes. I had to ensure that when I declared my audio player in the SwiftUI View, it was an @State object. After I added @State before my property it played every time! I am importing AVKit on a separate file where I have my audio play functions... something like I have pasted below. Lmk if you need more clarification.

//In my View I have something like this at the top 

struct ContentView: View {
    
    @State var vm = PlayViewModel()   

//When I want some audio to play I call it like this...                      vm.play(fileNamed: "blip")
//And the rest of my SwiftUI code below

}

//And then this is what I have setup in a separate Swift file.

import AVKit

class PlayViewModel {
    
    private var audioPlayer: AVAudioPlayer!
    
    
    func play(fileNamed:String) {
        let sound = Bundle.main.path(forResource: fileNamed, ofType: "wav")
        
        audioPlayer?.prepareToPlay() //maybe not needed?  idk

        do {
            audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: sound!))
            audioPlayer?.play()
        } catch {
            print(error)
        }
    }
}

Upvotes: 1

Adam
Adam

Reputation: 5105

Your AVAudioPlayer only exists in the scope of your button action, so it’s deallocated before the sound can play. You need to save it somewhere… I think you could declare it in SoundsListView as a @State var, but it may be better to refactor it out of the SwiftUI view entirely.

EDIT: You probably also need to set your AVAudioSession, which lets you change playback category and control how your audio interacts with other audio e.g. music playback.

Example:

do {
    try AVAudioSession.sharedInstance().setCategory(.playback)
    try AVAudioSession.sharedInstance().setCategory(.playback, options: [.mixWithOthers])
    try AVAudioSession.sharedInstance().setActive(true)
} catch {
    print(error)
}

Upvotes: 6

Related Questions