Im trying to create an app with playing sound and 4 timers to use If user want to. When timers are finished playing should stop. I got really stuck with this timers here and they are counting down but they don't stop and reset after user stop playback. Also they won't stop the sound if they finished :) their are just counting down..Could you see what im I doing wrong?Thanks
My TimerViewModel:
import Foundationimport Combineclass TimerViewModel: ObservableObject { @Published var remainingTime: Int = 0 @Published var totalDuration: Int = 0 @Published var timerActive: Bool = false private var timer: AnyCancellable? private var audioPlayerViewModel: AudioPlayerViewModel init(audioPlayerViewModel: AudioPlayerViewModel) { self.audioPlayerViewModel = audioPlayerViewModel } func startTimer(duration: Int) { remainingTime = duration totalDuration = duration timerActive = true timer = Timer.publish(every: 1, on: .main, in: .common) .autoconnect() .sink { [weak self] _ in self?.tick() } } private func tick() { if remainingTime > 0 { remainingTime -= 1 } else { stopTimer() stopPlayback() } } func stopTimer() { timerActive = false timer?.cancel() timer = nil } func resetTimer() { stopTimer() remainingTime = 0 } private func stopPlayback() { audioPlayerViewModel.audioPlayer?.pause() }}
My PlayerViewModel:
import SwiftUIimport AVFoundationclass AudioPlayerViewModel: ObservableObject { var timerViewModel: TimerViewModel? var audioPlayer: AVAudioPlayer? @Published var isPlaying = false init() { if let asset = NSDataAsset(name: "thesound") { do { self.audioPlayer = try AVAudioPlayer(data: asset.data, fileTypeHint: "mp3") } catch { print("AVAudioPlayer could not be instantiated.", error) } } else { print("Audio file could not be found.") } } func playOrPause() { guard let player = audioPlayer else { return } if player.isPlaying { player.pause() isPlaying = false timerViewModel?.stopTimer() timerViewModel?.resetTimer() } else { player.play() isPlaying = true } }}
TimersView:
import SwiftUIstruct TimersView: View { @StateObject private var audioPlayerViewModel = AudioPlayerViewModel() @ObservedObject var viewModel: TimerViewModel var body: some View { VStack { HStack(spacing: 60) { ForEach([15, 30, 45, 60] as? [Int] ?? [], id: \.self) { minute in Button(action: { viewModel.startTimer(duration: minute * 60) }) { if minute == 60 { Text("1 h") .foregroundStyle(.white) .whiteTextWithCircularBorder()// Jeśli minuta wynosi 60, wyświetl "1 h" } else { Text("\(minute)") .foregroundStyle(.white) .whiteTextWithCircularBorder()// W przeciwnym razie wyświetl normalną liczbę minut } } } } } }}extension Text { func whiteTextWithCircularBorder() -> some View { self .overlay( Circle() .stroke(Color.white, lineWidth: 1) .frame(width: 50, height: 50) ) }}
ContentView:
import SwiftUIimport AVFoundationstruct ContentView: View { @StateObject var audioPlayerViewModel = AudioPlayerViewModel() @StateObject var timerViewModel = TimerViewModel(audioPlayerViewModel: AudioPlayerViewModel()) var body: some View { ZStack { Color.black .ignoresSafeArea() VStack { Spacer() Spacer() Button(action: { audioPlayerViewModel.playOrPause() }) { Image("Icon-inside") .resizable() .scaledToFit() .frame(width: 100, height: 100) } Text(audioPlayerViewModel.isPlaying ? "" : "Push it" ) .font(.subheadline) .foregroundStyle(.white) Spacer() if audioPlayerViewModel.isPlaying { TimersView(viewModel: timerViewModel) Text("\(timerViewModel.remainingTime / 60) min") .foregroundStyle(.white) .padding(.top, 50) .padding(.bottom, 50) } } } }}
- I need timers to stop playing sound after countdown is finished.
- I need to reset and stop timers when user manually turn of sound by pressing the play/pause button