11

For the following code, I am getting the following error. I don't know how to work around this. How can I call volumeCheck() upon the button click?

struct ContentView: View {

    var player = AVAudioPlayer()

    var body: some View {
        HStack {
            Button(action: {
                self.volumeCheck()
            }) {
                Text("Click to test chimes volume")
            }
        }
    }

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

        do {
            player = try AVAudioPlayer(contentsOf: url)
            player.prepareToPlay()
            player.volume = Float(self.sliderValue)
            player.play()
        } catch let error {
            print(error.localizedDescription)
        }
        print("volume checked print")
    }
}
ScottyBlades
  • 12,189
  • 5
  • 77
  • 85

3 Answers3

13

The problem is that View is a struct and it's body field is a computed property with a nonmutating getter. In your code it happens mutating method to be called in that nonmutating getter. So all you need to do is put your player to some kind of model:

class Model {
    var player: AVPlayerPlayer()
}

struct ContentView: View {

    var model = Model()

    // player can be changed from anywhere
}

P.S. In some other cases you may want changes in your model be reflected in view so you'd have to add @ObservedObject just before model declaration.

Hope that helps

glassomoss
  • 736
  • 6
  • 7
1

You are trying to set player to a new object in volumeCheck(). Set the player in your initialiser:

struct ContentView: View {

    private var player: AVAudioPlayer?

    public init() {
        if let url = Bundle.main.url(forResource: "chimes",
                                     withExtension: "mp3") {
            self.player = try? AVAudioPlayer(contentsOf: url)
        }
    }

    var body: some View {
        HStack {
            Button("Click to test chimes volume") {
                self.volumeCheck()
                }   .disabled(player == nil)
        }
    }

    private func volumeCheck() {
        player?.prepareToPlay()
        player?.volume = Float(self.sliderValue)
        player?.play()
        print("volume checked print")
    }
}

Please note that you are using sliderValue in your code even though it is not defined anywhere.

LuLuGaGa
  • 13,089
  • 6
  • 49
  • 57
-1

Just a little gist: (for others.. arriving here like me... )

https://gist.github.com/ingconti/e0a2ceaf243ff2e27ed02144e506d996

the logic is to use @State:

struct ContentView: View {
    
    //var msg = ""      // WRONG! try to uncomment..
    @State var msg = "" // CORRECT!

    var body: some View {
        VStack {
            Text(msg)
            Button("Fill me!", action: {
                fillMe()
            })
        }
        .padding()
    }
    
    func fillMe(){
        self.msg = "Hello!!"
    }
}

Hope can help others.

ingconti
  • 10,876
  • 3
  • 61
  • 48