I am trying to play music on my app and manage to play/stop the music from the app's settings.
First I am creating an ObservableObject
class called MusicPlayer
:
class MusicPlayer: ObservableObject {
@Published var isPlaying = AppDefaults.shared.isMusicPlaying()
@Published var music : AVAudioPlayer? = nil
func playMusic() {
guard let strFilePath = Bundle.main.path(forResource: "music", ofType: "mp3") else { return }
do {
music = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: strFilePath))
} catch {
print(error)
}
music?.volume = 0.60
music?.numberOfLoops = -1
if isPlaying {
music?.play()
} else {
music?.stop()
}
}
}
and then play the music in main app file:
@main
struct AppName: App {
@StateObject private var player = MusicPlayer()
var body: some Scene {
WindowGroup {
ContentsView()
.onAppear {
player.playMusic()
}
}
}
}
and then trying to stop/play the music using toggle from settings:
struct SettingsView: View {
@StateObject private var player = MusicPlayer()
var body: some View {
Toggle("Music", isOn: $player.isPlaying)
.onChange(of: player.isPlaying, perform: { _ in
AppDefaults.shared.setMusic(player.isPlaying)
if player.isPlaying {
player.music?.stop()
} else {
player.music?.play()
}
})
}
}
now the problem is switching to on or off doesn't change the state of playing. How can I fix this issue?
CodePudding user response:
The issue here is you are initializing your Viewmodel twice. So you have 2 different sources of truth. So there are 2 different AVAudioPlayer
.
Solution: Create one single instance in the top View and pass this on to the views that need this.
As you decided to omit how SettingsView
correlate with the other Views I can only give a more general solution.
Let´s asume the SettingsView
is used in AppName
:
@StateObject private var player = MusicPlayer()
WindowGroup {
....(ContentView stuff)
SettingsView()
// pass the observableObject on to the SettingsView and its children
.environmentObject(player)
}
Then in SettingsView
:
struct SettingsView: View {
// get the observableObject from the environment
@EnvironmentObject private var player: MusicPlayer
var body: some View {
Toggle("Music", isOn: $player.isPlaying)
.onChange(of: player.isPlaying, perform: { _ in
AppDefaults.shared.setMusic(player.isPlaying)
if player.isPlaying {
player.music?.stop()
} else {
player.music?.play()
}
})
}
}