I realized I never got back to this post to show what I ended up with. Like I mentioned before, I was thinking of putting the audio player in shared application data. It looks like this ...
import SwiftUI
import Observation
import os
@preconcurrency import AVFoundation
@main
struct Chain_TimerApp: App {
@State private var appData = ApplicationData.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(appData)
}
}
}
@Observable class ApplicationData: @unchecked Sendable {
var timers: [TimerData] = []
var timerRunningStates: [UUID: Bool] = [:]
var isSerial: Bool = false
var audioData: AudioData
let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "Chain Timer", category: "ApplicationData")
static let shared: ApplicationData = ApplicationData()
.
.
.
For the AudioData structure
struct AudioData {
var audioPlayer: AVAudioPlayer
var sound: String = "classicAlarm"
var logger: Logger
init(logger: Logger) {
self.logger = logger
audioPlayer = AVAudioPlayer()
}
mutating func setSound(sound: String) {
let soundPath = Bundle.main.url(forResource: sound, withExtension: "mp3")! // Default
do {
try audioPlayer = AVAudioPlayer(contentsOf: soundPath)
} catch {
logger.error("Cannot create player. Error: \(error)")
}
}
func playSound() {
if !audioPlayer.isPlaying { // Only play one at a time
audioPlayer.play()
}
}
func cancelSound() {
audioPlayer.stop()
audioPlayer.currentTime = 0 // Reset playback
}
}
Then in my view, I grab application data from the environment and add an alert modifier to a Text view that will use the audio player ...
@Environment(ApplicationData.self) private var appData
.alert(isPresented: $showAlarmAlert) {
appData.audioData.playSound()
return Alert(title: Text("Time's Up!"),
message: Text("Hit Okay"),
dismissButton: .cancel(Text("Okay"), action: {
appData.audioData.cancelSound()
Putting the audio player in shared data lets any view make use of it.
Thanks,
MGeek
Topic:
UI Frameworks
SubTopic:
SwiftUI
Tags: